package com.xbongbong.saas.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.crm.handle.explain.impl.ContractExplainHandleImpl;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.ConfigConstant;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasAppEntity;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasProcessDataEntity;
import com.xbongbong.paas.domain.entity.PaasStageEntity;
import com.xbongbong.paas.domain.entity.StageProcessEntity;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasEsModel;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.ConditionEnum;
import com.xbongbong.paas.enums.DefaultTypeEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.enums.PlatFormEnum;
import com.xbongbong.paas.enums.StageTypeEnum;
import com.xbongbong.paas.field.DefaultAttrPoJo;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.MultiUnitItemPoJo;
import com.xbongbong.paas.field.SubFormPoJo;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.help.FormDataValidateProductHelp;
import com.xbongbong.paas.help.ProFormHelp;
import com.xbongbong.paas.help.ProSaveHelp;
import com.xbongbong.paas.help.TransactionHelp;
import com.xbongbong.paas.help.UserHelp;
import com.xbongbong.paas.log.MongoLogHelp;
import com.xbongbong.paas.log.constant.LogMemoConstant;
import com.xbongbong.paas.log.pojo.InfoArrPojo;
import com.xbongbong.paas.model.PaasAppModel;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasProcessDataModel;
import com.xbongbong.paas.model.PaasStageModel;
import com.xbongbong.paas.model.StageProcessModel;
import com.xbongbong.paas.pojo.AttrValuePojo;
import com.xbongbong.paas.pojo.ButtonPojo;
import com.xbongbong.paas.pojo.FieldAttrEntityForImport;
import com.xbongbong.paas.pojo.FormTitlePoJo;
import com.xbongbong.paas.pojo.SummaryDataPoJo;
import com.xbongbong.paas.pojo.WorkFlowPojo;
import com.xbongbong.paas.pojo.dto.AggSumDTO;
import com.xbongbong.paas.pojo.dto.FormDataAddDTO;
import com.xbongbong.paas.pojo.dto.FormDataDeleteBatchDTO;
import com.xbongbong.paas.pojo.dto.FormDataListDTO;
import com.xbongbong.paas.pojo.dto.FormDataRepeatDTO;
import com.xbongbong.paas.pojo.dto.FormDataSummaryDTO;
import com.xbongbong.paas.pojo.dto.ImportFormDataDTO;
import com.xbongbong.paas.pojo.dto.ParseSingleRowDataDTO;
import com.xbongbong.paas.pojo.dto.SaasFormSaveDTO;
import com.xbongbong.paas.pojo.dto.UserAndDepartmentGetDTO;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.pojo.imports.CellTitlePojo;
import com.xbongbong.paas.pojo.imports.CellValuePojo;
import com.xbongbong.paas.pojo.imports.RowGroupPojo;
import com.xbongbong.paas.pojo.vo.AggSumVO;
import com.xbongbong.paas.pojo.vo.FormDataGetVO;
import com.xbongbong.paas.pojo.vo.FormDataRepeatVO;
import com.xbongbong.paas.pojo.vo.PaasFormDataEsListVO;
import com.xbongbong.paas.pojo.vo.ParseSingleRowDataVO;
import com.xbongbong.paas.pojo.vo.UserAndDepartmentGetVO;
import com.xbongbong.paas.service.CustomerRuleService;
import com.xbongbong.paas.service.DepartmentService;
import com.xbongbong.paas.service.PaasAppService;
import com.xbongbong.paas.service.PaasFormDataService;
import com.xbongbong.paas.service.PaasFormService;
import com.xbongbong.paas.service.TransferBackRuleScriptService;
import com.xbongbong.paas.service.WriteOffRecordService;
import com.xbongbong.parent.interfaces.DynamicStrategy;
import com.xbongbong.paas.service.dynamic.crm.strategy.DynamicStrategyFactory;
import com.xbongbong.paas.service.dynamic.help.DynamicDetailsHelper;
import com.xbongbong.paas.service.log.crm.help.OperationLogHelp;
import com.xbongbong.paas.service.team.TeamDataHelp;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.PageHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.BaseVO;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.parent.factory.StageExecuteFactory;
import com.xbongbong.parent.interfaces.StageExecuteHandle;
import com.xbongbong.parent.interfaces.logger.OperationLogFactory;
import com.xbongbong.parent.interfaces.logger.OperationLogHandle;
import com.xbongbong.parent.parse.data.FormDataUpdateGetAnalysisDataHelp;
import com.xbongbong.pro.applist.pojo.AppListPojo;
import com.xbongbong.pro.applist.pojo.vo.ListAppVO;
import com.xbongbong.pro.businessdata.pojo.dto.FormatExplainDTO;
import com.xbongbong.pro.businessproduct.pojo.dto.BusinessProductListDTO;
import com.xbongbong.pro.businesstage.pojo.dto.ChangeStageDTO;
import com.xbongbong.pro.businesstage.pojo.dto.CommonJumpDTO;
import com.xbongbong.pro.common.pojo.dto.RepeatCommonConditionDTO;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.CommonConstant;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.contract.pojo.CashProfitSavePojo;
import com.xbongbong.pro.contract.pojo.dto.ContractDeleteBatchDTO;
import com.xbongbong.pro.contract.pojo.dto.ContractEditAttrUpdateDTO;
import com.xbongbong.pro.contract.pojo.dto.ContractRestoreOutboundDTO;
import com.xbongbong.pro.contract.pojo.dto.ContractUpdateBatchDTO;
import com.xbongbong.pro.contract.pojo.dto.ContractUpdateCashProfitDTO;
import com.xbongbong.pro.contract.pojo.dto.ContractUpdateDTO;
import com.xbongbong.pro.contract.pojo.dto.ContractUpdateWhenOutstockDTO;
import com.xbongbong.pro.contract.pojo.vo.CashProfitReturnVO;
import com.xbongbong.pro.contract.pojo.vo.ContractDeleteBatchVO;
import com.xbongbong.pro.contract.pojo.vo.ContractEditAttrUpdateVO;
import com.xbongbong.pro.contract.pojo.vo.ContractOutstockDeleteVO;
import com.xbongbong.pro.contract.pojo.vo.ContractProductUpdateOutstockNumVO;
import com.xbongbong.pro.contract.pojo.vo.ContractUpdateBatchVO;
import com.xbongbong.pro.contractperformance.pojo.ContractPerformanceLogPojo;
import com.xbongbong.pro.contractperformance.pojo.dto.ContractPerformanceDeleteBatchDTO;
import com.xbongbong.pro.contractproduct.pojo.dto.ContractProductDeleteBatchDTO;
import com.xbongbong.pro.contractsurcharge.pojo.dto.ContractSurchargeDeteleBatchDTO;
import com.xbongbong.pro.contractuser.pojo.dto.ContractUserDeleteBatchDTO;
import com.xbongbong.pro.customer.pojo.dto.ChangeStatusDTO;
import com.xbongbong.pro.customercommunicate.pojo.dto.CustomerDynamicAddDTO;
import com.xbongbong.pro.customercommunicate.pojo.dto.DynamicDTO;
import com.xbongbong.pro.detailtab.dto.ChangeDocumentStatusSaveDTO;
import com.xbongbong.pro.detailtab.dto.DetailTabDataGetDTO;
import com.xbongbong.pro.detailtab.vo.ChangeDocumentStatusSaveVO;
import com.xbongbong.pro.detailtab.vo.ProductSummaryVO;
import com.xbongbong.pro.dictionary.pojo.DictionaryPojo;
import com.xbongbong.pro.dictionary.pojo.dto.DictionaryListDTO;
import com.xbongbong.pro.dictionary.pojo.vo.DictionaryVO;
import com.xbongbong.pro.domain.entity.FormConfigEntity;
import com.xbongbong.pro.domain.entity.LinkProductRelyEntity;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.domain.entity.UpdateDataValueEntity;
import com.xbongbong.pro.dynamic.pojo.dto.DynamicDeleteDTO;
import com.xbongbong.pro.dynamic.pojo.dto.DynamicSaveDTO;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.PaasStageEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.ContractErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.ImportErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.PaymentErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.ProductErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.PurchaseErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.RefundErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.TemplateErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.UserTeamErrorCodeEnum;
import com.xbongbong.pro.enums.permission.enums.ProPermissionAliasEnum;
import com.xbongbong.pro.enums.teammembers.enums.CoUserPermissionEnum;
import com.xbongbong.pro.enums.teammembers.enums.FormCoUserDefaultPermissionEnum;
import com.xbongbong.pro.formdata.pojo.AfterSavePojo;
import com.xbongbong.pro.formdata.pojo.dto.SaasAnalysisDataDTO;
import com.xbongbong.pro.formexplain.pojo.dto.HandlerExplainDTO;
import com.xbongbong.pro.formexplain.pojo.dto.HandlerExplainInLinkItemDTO;
import com.xbongbong.pro.listbatch.pojo.dto.AddLabelDTO;
import com.xbongbong.pro.log.pojo.DetailLogComplexPojo;
import com.xbongbong.pro.log.pojo.dto.DeleteBatchOperationLogDTO;
import com.xbongbong.pro.log.pojo.dto.DeleteOperationLogDTO;
import com.xbongbong.pro.log.pojo.dto.UpdateBatchOperationLogDTO;
import com.xbongbong.pro.message.constant.ContractPushConstant;
import com.xbongbong.pro.message.constant.OutstockPushConstant;
import com.xbongbong.pro.message.constant.PurchasePushConstant;
import com.xbongbong.pro.message.constant.QuotationPushConstant;
import com.xbongbong.pro.message.enums.PushTypeEnum;
import com.xbongbong.pro.message.help.PushHelper;
import com.xbongbong.pro.message.pojo.PushRefTypePojo;
import com.xbongbong.pro.message.pojo.dto.MessageRabbitMqDTO;
import com.xbongbong.pro.opportunity.pojo.JudgeSaleStageSuccessPojo;
import com.xbongbong.pro.payment.pojo.dto.PaymentDeleteBatchDTO;
import com.xbongbong.pro.product.pojo.ProductSavePojo;
import com.xbongbong.pro.product.pojo.dto.PagingProductDTO;
import com.xbongbong.pro.product.pojo.vo.DetailProductVO;
import com.xbongbong.pro.product.pojo.vo.PagingProductVO;
import com.xbongbong.pro.pushnotify.dto.PushNotifyDeleteDTO;
import com.xbongbong.pro.rabbitmq.producer.BaseProducer;
import com.xbongbong.pro.service.toolbox.elasticsearch.help.ProEsHelper;
import com.xbongbong.pro.service.toolbox.statistic.help.FormHelp;
import com.xbongbong.pro.sheet.pojo.dto.SheetDeleteHandleRelateDTO;
import com.xbongbong.pro.statistic.result.pojo.ElasticAggsPojo;
import com.xbongbong.pro.thread.ThreadPoolBeanConfig;
import com.xbongbong.pro.user.pojo.ReturnUserAndDepartmentPojo;
import com.xbongbong.pro.weblist.pojo.WaitContractProductPojo;
import com.xbongbong.pro.weblist.pojo.WaitOutstockProductPojo;
import com.xbongbong.pro.weblist.pojo.WaitPurchaseProductPojo;
import com.xbongbong.pro.weblist.pojo.vo.WaitPurchaseProductVO;
import com.xbongbong.saas.analytical.impl.ContractValidateAnalyticalServiceImpl;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ErrorMessageConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.constant.TabConstant;
import com.xbongbong.saas.domain.entity.ContractProductEntity;
import com.xbongbong.saas.domain.entity.ContractSurchargeEntity;
import com.xbongbong.saas.domain.entity.InstockProductEntity;
import com.xbongbong.saas.domain.entity.InvoiceRelationshipEntity;
import com.xbongbong.saas.domain.entity.OutstockProductEntity;
import com.xbongbong.saas.domain.entity.PromotionProductEntity;
import com.xbongbong.saas.domain.entity.PurchaseProductEntity;
import com.xbongbong.saas.domain.entity.SmartReplenishmentEntity;
import com.xbongbong.saas.domain.entity.UserTeamEntity;
import com.xbongbong.saas.domain.entity.ext.ContractEntityExt;
import com.xbongbong.saas.domain.entity.ext.CustomerEntityExt;
import com.xbongbong.saas.domain.entity.ext.OutstockEntityExt;
import com.xbongbong.saas.domain.entity.ext.OutstockProductEntityExt;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.domain.entity.ext.QuotationEntityExt;
import com.xbongbong.saas.domain.entity.ext.RefundEntityExt;
import com.xbongbong.saas.enums.BusinessProductEnum;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.saas.enums.ContractProduceEnum;
import com.xbongbong.saas.enums.ContractProductTypeEnum;
import com.xbongbong.saas.enums.FormConfigEnum;
import com.xbongbong.saas.enums.InOutStockStatusEnum;
import com.xbongbong.saas.enums.OperateModuleTypeEnum;
import com.xbongbong.saas.enums.OperateTypeEnum;
import com.xbongbong.saas.enums.OrderOutStockExpressStatusEnum;
import com.xbongbong.saas.enums.OutstockSaleRelativeConstantEnum;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.ProjectLogEnum;
import com.xbongbong.saas.enums.RedundantTemplateTypeEnum;
import com.xbongbong.saas.enums.RelativeProductEnum;
import com.xbongbong.saas.enums.RuleSettingEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.SurchargeShowEnum;
import com.xbongbong.saas.enums.UpdateDataTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.base.ContractBaseEnum;
import com.xbongbong.saas.enums.business.ContactEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.OrderEnum;
import com.xbongbong.saas.enums.business.OrderOutStockEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.PurchaseEnum;
import com.xbongbong.saas.enums.business.QuotationEnum;
import com.xbongbong.saas.enums.business.RefundEnum;
import com.xbongbong.saas.enums.business.SalesOpportunityEnum;
import com.xbongbong.saas.enums.business.WaitInstockEnum;
import com.xbongbong.saas.enums.business.WaitOutStockStatusEnum;
import com.xbongbong.saas.enums.business.WaitOutstockEnum;
import com.xbongbong.saas.enums.dictionary.AllOutBoundEnum;
import com.xbongbong.saas.enums.dictionary.ContractStatusEnum;
import com.xbongbong.saas.enums.dictionary.CustomerTypeEnum;
import com.xbongbong.saas.enums.dictionary.DictionaryEnum;
import com.xbongbong.saas.enums.dictionary.EnableButtonEnum;
import com.xbongbong.saas.enums.dictionary.OrderExpressStatusEnum;
import com.xbongbong.saas.enums.dictionary.OrderPayMentStatusEnum;
import com.xbongbong.saas.enums.dictionary.OrderStatusEnum;
import com.xbongbong.saas.enums.dictionary.PaymentStatusEnum;
import com.xbongbong.saas.enums.dictionary.QuotationStatusEnum;
import com.xbongbong.saas.enums.dictionary.ShipStatusEnum;
import com.xbongbong.saas.enums.product.ContractProductEnum;
import com.xbongbong.saas.enums.subform.SubFormTransferEnum;
import com.xbongbong.saas.help.ExplainHelp;
import com.xbongbong.saas.help.ImportHelper;
import com.xbongbong.saas.help.OpportunityStageLogHelp;
import com.xbongbong.saas.help.ProDetailHandlerHelp;
import com.xbongbong.saas.help.PublicSeniorRuleHelp;
import com.xbongbong.saas.help.RelativeProductHelp;
import com.xbongbong.saas.help.SaasDeleteHelp;
import com.xbongbong.saas.help.SaasGetHelp;
import com.xbongbong.saas.help.SaasHelp;
import com.xbongbong.saas.help.SaasListHelp;
import com.xbongbong.saas.help.SaasProductHelp;
import com.xbongbong.saas.help.SaasSaveHelp;
import com.xbongbong.saas.help.SaasUpdateHelp;
import com.xbongbong.saas.help.ShowFormatHelp;
import com.xbongbong.saas.help.workflow.ContractHelp;
import com.xbongbong.saas.help.workflow.MarketActivityForStaticHelp;
import com.xbongbong.saas.model.ContractModel;
import com.xbongbong.saas.model.ContractProductModel;
import com.xbongbong.saas.model.ContractSurchargeModel;
import com.xbongbong.saas.model.CustomerModel;
import com.xbongbong.saas.model.DataDictionaryModel;
import com.xbongbong.saas.model.FormConfigModel;
import com.xbongbong.saas.model.IndexTypeModel;
import com.xbongbong.saas.model.InstockModel;
import com.xbongbong.saas.model.InstockProductModel;
import com.xbongbong.saas.model.InvoiceRelationshipModel;
import com.xbongbong.saas.model.LinkProductRelyModel;
import com.xbongbong.saas.model.OutstockProductModel;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.PurchaseBalanceModel;
import com.xbongbong.saas.model.PurchaseProductModel;
import com.xbongbong.saas.model.QuotationModel;
import com.xbongbong.saas.model.SmartReplenishmentModel;
import com.xbongbong.saas.model.SpecificationModel;
import com.xbongbong.saas.model.SurchargeModel;
import com.xbongbong.saas.service.BomBillService;
import com.xbongbong.saas.service.ContractPerformanceService;
import com.xbongbong.saas.service.ContractProductService;
import com.xbongbong.saas.service.ContractService;
import com.xbongbong.saas.service.ContractSurchargeService;
import com.xbongbong.saas.service.ContractUserService;
import com.xbongbong.saas.service.CustomerCommunicateService;
import com.xbongbong.saas.service.InstockService;
import com.xbongbong.saas.service.MobileDetailService;
import com.xbongbong.saas.service.OpportunityService;
import com.xbongbong.saas.service.OutstockService;
import com.xbongbong.saas.service.PaymentService;
import com.xbongbong.saas.service.PaymentSheetService;
import com.xbongbong.saas.service.ProductService;
import com.xbongbong.saas.service.PushNotifyService;
import com.xbongbong.saas.service.QuotationService;
import com.xbongbong.saas.service.ScoreCalculateService;
import com.xbongbong.saas.service.UserTeamService;
import com.xbongbong.saas.service.WaitPurchaseService;
import com.xbongbong.saas.service.toolbox.help.AttrDecimalPrecisionHelper;
import com.xbongbong.saas.service.toolbox.help.LogHelp;
import com.xbongbong.saas.service.toolbox.help.ProductHelp;
import com.xbongbong.saas.service.toolbox.help.UserTeamHelp;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.sys.domain.entity.DepartmentEntity;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.enums.DataPermissionEnum;
import com.xbongbong.sys.model.CompanyConfigModel;
import com.xbongbong.sys.model.UserModel;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;

import static com.xbongbong.saas.enums.RelativeProductEnum.BARCODE;
import static com.xbongbong.saas.enums.RelativeProductEnum.MEMO;
import static com.xbongbong.saas.enums.RelativeProductEnum.NAME;
import static com.xbongbong.saas.enums.RelativeProductEnum.NUM;
import static com.xbongbong.saas.enums.RelativeProductEnum.PRODUCT_NO;
import static com.xbongbong.saas.enums.RelativeProductEnum.PRODUCT_PRICE;
import static com.xbongbong.saas.enums.RelativeProductEnum.SALE_DISCOUNT;
import static com.xbongbong.saas.enums.RelativeProductEnum.SALE_PRODUCT_PRICE;
import static com.xbongbong.saas.enums.RelativeProductEnum.SPECIFICATION;
import static com.xbongbong.saas.enums.RelativeProductEnum.SUBTOTAL;
import static com.xbongbong.saas.enums.RelativeProductEnum.UNIT;
import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getDoubleOrDefaultFromFormData;
import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getIntegerOrDefaultFromFormData;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * 合同业务处理接口实现
 *
 * @author feng.zheng
 * @version v1.0
 * @date 2019/1/23 14:04
 * @since v1.0
 */
@Service("contractService")
public class ContractServiceImpl implements ContractService {

    private static final Logger LOG = LoggerFactory.getLogger(ContractServiceImpl.class);

    @Resource
    private SaasSaveHelp saasSaveHelp;
    @Resource
    private SaasUpdateHelp saasUpdateHelp;
    @Resource
    private ShowFormatHelp showFormatHelp;
    @Resource
    private SaasGetHelp saasGetHelp;
    @Resource
    private ContractModel contractModel;
    @Resource
    private InstockService instockService;
    @Resource
    private InstockModel instockModel;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private UserModel userModel;
    @Resource
    private ContractProductModel contractProductModel;
    @Resource
    private ProductModel productModel;
    @Resource
    private InstockProductModel instockProductModel;
    @Resource
    private SpecificationModel specificationModel;
    @Resource
    private BomBillService bomBillService;
    @Resource
    private OutstockProductModel outstockProductModel;
    @Resource
    private OutstockService outstockService;
    @Resource
    private SaasHelp saasHelp;
    @Resource
    private ExplainHelp explainHelp;
    @Resource
    private SurchargeModel surchargeModel;
    @Resource
    private ContractSurchargeModel contractSurchargeModel;
    @Resource
    private PaymentService paymentService;
    @Resource
    private ProductService productService;
    @Resource
    private EsHelper esHelper;
    @Resource
    private UserTeamService userTeamService;
    @Resource
    private ContractUserService contractUserService;
    @Resource
    private ContractPerformanceService contractPerformanceService;
    @Resource
    private ContractProductService contractProductService;
    @Resource
    private ContractSurchargeService contractSurchargeService;
    @Resource
    private PaasEsModel paasEsModel;
    @Resource
    private ThreadPoolBeanConfig threadPoolBeanConfig;
    @Resource
    private DataDictionaryModel dataDictionaryModel;
    @Resource
    private AttrDecimalPrecisionHelper attrDecimalPrecisionHelper;
    @Resource
    private SaasListHelp saasListHelp;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private PurchaseProductModel purchaseProductModel;
    @Resource
    private PushHelper pushHelper;
    @Resource
    private BaseProducer baseProducer;
    @Resource
    private FormConfigModel formConfigModel;
    @Resource
    private ImportHelper importHelper;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private PushNotifyService pushNotifyService;
    @Resource
    @Lazy
    private CustomerCommunicateService customerCommunicateService;
    @Resource
    private PaasAppService paasAppService;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private CustomerRuleService customerRuleService;
    @Resource
    private MongoLogHelp mongoLogHelp;
    @Resource
    private SaasProductHelp saasProductHelp;
    @Resource
    private UserHelp userHelp;
    @Resource
    private ProEsHelper proEsHelper;
    @Resource
    private PaymentSheetService paymentSheetService;
    @Resource
    private PaasProcessDataModel paasProcessDataModel;
    @Resource
    private DepartmentService departmentService;
    @Resource
    private QuotationModel quotationModel;
    @Resource
    private FormHelp formHelp;
    @Resource
    private InvoiceRelationshipModel invoiceRelationshipModel;
    @Resource
    private FormDataValidateProductHelp formDataValidateProductHelp;
    @Resource
    private OpportunityService opportunityService;
    @Resource
    private QuotationService quotationService;
    @Resource
    private ProFormHelp proFormHelp;
    @Resource
    private ProDetailHandlerHelp proDetailHandlerHelp;
    @Resource
    private MobileDetailService mobileDetailService;
    @Resource
    private LinkProductRelyModel linkProductRelyModel;
    @Resource
    private BusinessProductServiceImpl businessProductService;
    @Resource
    private FormDataUpdateGetAnalysisDataHelp formDataUpdateGetAnalysisDataHelp;
    @Resource
    private PaasFormService paasFormService;
    @Resource
    private CustomerModel customerModel;
    @Resource
    private RelativeProductHelp relativeProductHelp;
    @Resource
    private ScoreCalculateService scoreCalculateService;
    @Resource
    private LogHelp logHelp;
    @Resource
    private ContractValidateAnalyticalServiceImpl contractAnalyticalService;
    @Resource
    private ContractHelp contractHelp;
    @Resource
    private ProductHelp productHelp;
    @Resource
    private WriteOffRecordService writeOffRecordService;
    @Resource
    private PurchaseBalanceModel purchaseBalanceModel;
    @Resource
    private PaasFormDataService paasFormDataService;
    @Resource
    private SaasListServiceImpl saasListService;
    @Resource
    private SmartReplenishmentModel smartReplenishmentModel;
    @Resource
    private OpportunityStageLogHelp opportunityStageLogHelp;
    @Resource
    private WaitPurchaseService waitPurchaseService;
    @Resource
    private TeamDataHelp teamDataHelp;
    @Resource
    private ContractExplainHandleImpl contractExplainHandle;
    @Resource
    private StageExecuteFactory stageExecuteFactory;
    @Resource
    private StageProcessModel stageProcessModel;
    @Resource
    private PaasStageModel paasStageModel;
    @Resource
    private TransferBackRuleScriptService transferBackRuleScriptService;
    @Resource
    private UserTeamHelp userTeamHelp;
    @Resource
    private PublicSeniorRuleHelp publicSeniorRuleHelp;
    @Resource
    private IndexTypeModel indexTypeModel;
    @Resource
    private OperationLogFactory operationLogFactory;
    @Resource
    private DynamicStrategyFactory dynamicStrategyFactory;
    @Resource
    private DynamicDetailsHelper dynamicDetailsHelper;
    @Resource
    private MarketActivityForStaticHelp marketActivityForStaticHelp;
    @Resource
    private OperationLogHelp operationLogHelp;
    @Resource
    private PaasAppModel paasAppModel;
    @Resource
    private TransactionHelp transactionHelp;


    /**
     * 合同保存前saas特殊校验
     *
     * @param validateDataDTO saas特殊校验数据入参
     * @throws XbbException 业务异常
     * @author feng.zheng
     * @date 2019/1/20
     */
    @Override
    public void beforeSave(ValidateDataDTO validateDataDTO) throws XbbException {
        contractAnalyticalService.beforeSave(validateDataDTO);
    }



    /**
     * 合同保存后逻辑
     *
     * @param saasFormSaveDTO saas保存数据
     * @throws XbbException 业务异常
     * @author feng.zheng
     */
    @Override
    public AfterSavePojo afterSave(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        AfterSavePojo afterSavePojo = new AfterSavePojo();
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        JSONObject newData = saasFormSaveDTO.getNewData();
        JSONObject oldData = saasFormSaveDTO.getOldData();
        Long contractId = newPaasFormDataEntity.getId();
        String corpid = newPaasFormDataEntity.getCorpid();
        Boolean isNew = saasFormSaveDTO.getIsNew();
        Integer taskType = saasFormSaveDTO.getTaskType();
        Integer allOutbound = newData.getInteger(ContractEnum.ALL_OUT_BOUND.getAttr());
        Long newCustomerId = FastJsonHelper.getLongFromFormData(newData, ContractEnum.LINK_CUSTOMER.getAttr());
        Long newContactId = FastJsonHelper.getLongFromFormData(newData, ContractEnum.LINK_CONTACT.getAttr());
        Long newQuotationId = saasUpdateHelp.eitherLongOrTheDefault(newData, ContractEnum.QUOTATION_ID.getAttr(), 0L);
        Long oldQuotationId = saasUpdateHelp.eitherLongOrTheDefault(oldData, ContractEnum.QUOTATION_ID.getAttr(), 0L);
        Long newOppId = saasUpdateHelp.eitherLongOrTheDefault(newData, ContractEnum.LINK_OPPORTUNITY.getAttr(), 0L);
        Long oldOppId = saasUpdateHelp.eitherLongOrTheDefault(oldData, ContractEnum.LINK_OPPORTUNITY.getAttr(), 0L);
        Long formId = newPaasFormDataEntity.getFormId();
        Integer businessType = saasFormSaveDTO.getBusinessType();
        boolean isFromProcess = Objects.equals(saasFormSaveDTO.getIsFromProcess(), BasicConstant.ONE);
        PaasFormExplainEntity explainEntity = proFormHelp.getExplainEntity(corpid, formId, businessType);
        List<FieldAttrEntity> fieldAttrEntities = JSON.parseArray(explainEntity.getExplains(), FieldAttrEntity.class);

        // 团队保存
        afterSavePojo = teamDataHelp.saveUserTeam(saasFormSaveDTO.getSaasNeedRedundantAttrPoJo(), saasFormSaveDTO.getNewPaasFormDataEntity().getId(), saasFormSaveDTO.getNewPaasFormDataEntity(), saasFormSaveDTO.getIsImport(), false);

        // 新建的时候阶段不为空或者编辑时阶段不一致才需要更改阶段
        try {
            boolean hasStageField = false;
            String stageAttr = "";
            for (FieldAttrEntity fieldAttrEntity : fieldAttrEntities) {
                if (Objects.equals(FieldTypeEnum.STAGE_THRUSTER.getType(), fieldAttrEntity.getFieldType()) && Objects.equals(BasicConstant.ONE, fieldAttrEntity.getIsOpen())){
                    hasStageField = true;
                    stageAttr = fieldAttrEntity.getAttr();
                    break;
                }
            }
            //判断有没有阶段推进器字段
            if (hasStageField) {
                Long stageProcessId = FastJsonHelper.getLongOrDefaultFromFormData(newData, PaasStageEnum.STAGE_PROCESS_ID.getAttr(), BasicConstant.ZERO_LONG);
                StageProcessEntity stageProcessEntity = stageProcessModel.getByKey(stageProcessId, corpid);
                Long toStageId = FastJsonHelper.getLongOrDefaultFromFormData(newData, PaasStageEnum.STAGE_ID.getAttr(), BasicConstant.ZERO_LONG);
                Long fromStageId = FastJsonHelper.getLongOrDefaultFromFormData(oldData, PaasStageEnum.STAGE_ID.getAttr(), BasicConstant.ZERO_LONG);
                PaasStageEntity toStageEntity = paasStageModel.getByKey(toStageId, corpid);
                PaasStageEntity fromStageEntity = paasStageModel.getByKey(fromStageId, corpid);
                //判断阶段是否发生阶段改变,发生阶段改变才需要跳转
                boolean isChangeStage = false;
                if (isNew) {
                    isChangeStage = !Objects.equals(toStageId, BasicConstant.ZERO_LONG);
                } else {
                    isChangeStage = !Objects.equals(toStageId, BasicConstant.ZERO_LONG) && !Objects.equals(toStageId, fromStageId);
                }
                if (isChangeStage) {
                    //封装阶段跳转参数
                    StageExecuteHandle stageExecute = stageExecuteFactory.getStageExecute(SaasMarkEnum.SAAS.getCode(), businessType);
                    CommonJumpDTO commonJumpDTO = new CommonJumpDTO();
                    commonJumpDTO.setIsImport(saasFormSaveDTO.getIsImport());
                    commonJumpDTO.setPlatform(saasFormSaveDTO.getPlatform());
                    commonJumpDTO.setSaasMark(SaasMarkEnum.SAAS.getCode());
                    commonJumpDTO.setBusinessType(businessType);
                    commonJumpDTO.setFormId(formId);
                    commonJumpDTO.setDataId(contractId);
                    commonJumpDTO.setCorpid(corpid);
                    commonJumpDTO.setUserId(isFromProcess ? saasFormSaveDTO.getProcessCreatorId() : saasFormSaveDTO.getUserId());
                    commonJumpDTO.setFromStageType(isNew ? StageTypeEnum.START_STAGE.getType() : Objects.isNull(fromStageEntity) ? StageTypeEnum.START_STAGE.getType() : fromStageEntity.getType());
                    commonJumpDTO.setToStageType(toStageEntity.getType());
                    commonJumpDTO.setFromStageEntity(fromStageEntity);
                    commonJumpDTO.setToStageEntity(toStageEntity);
                    commonJumpDTO.setStageProcessEntity(stageProcessEntity);
                    commonJumpDTO.setStageAttr(stageAttr);
                    commonJumpDTO.setFromApproval(BasicConstant.ONE);
                    commonJumpDTO.setIsNew(isNew);
                    commonJumpDTO.setFromOuterLink(saasFormSaveDTO.getFromOuterLink());
                    stageExecute.stageCommonJump(commonJumpDTO);
                }
            }
        } catch (XbbException e) {
            LOG.error("ContractServiceImpl.stageCommonJump出错:", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }

        // 以下业务应该是可以异步处理的业务
        Runnable runnable = () -> {
            // 合同关联产品保存
            try {
                JSONArray contractProduct = newData.getJSONArray(ContractEnum.PRODUCT.getAttr());
                if (contractProduct != null) {
                    if (saasFormSaveDTO.getSaasNeedRedundantAttrPoJo().getAttrCanSee(ContractEnum.PRODUCT.getAttr())) {
                        saveProduct(contractProduct, corpid, newPaasFormDataEntity, isNew, saasFormSaveDTO.getNewPaasFormDataEntity().getFormId(), null,taskType);
                    }
//                    JSONArray productList = contractProduct.getJSONArray(StringConstant.PRODUCT_LIST);
//                    insertOrUpdateProduct(productList, corpid, contractId);
                }
            } catch (Exception e) {
                LOG.error("合同保存产品数据解析/合同保存产品关联产品", e);
            }
            try {
                // 更新合同业绩分配
                List<ContractPerformanceLogPojo> contractPerformanceLogList = new ArrayList<>();
                contractPerformanceService.changeContractPerformance(newPaasFormDataEntity, contractPerformanceLogList);
                if (CollectionsUtil.isNotEmpty(contractPerformanceLogList)) {
                    // 合同业绩分配日志
                    OperateTypeEnum operateTypeEnum = isNew ? OperateTypeEnum.NEW : OperateTypeEnum.EDIT;

                    UserVO userVO = saasFormSaveDTO.getLoginUser();
                    OperateModuleTypeEnum operateModuleTypeEnum = OperateModuleTypeEnum.getByAlias(XbbRefTypeEnum.CONTRACT.getAlias());
                    String opObjectName = I18nMessageUtil.getMessage(XbbRefTypeEnum.CONTRACT_PERFORMANCE.getName());
                    String memo = String.format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_DECISION_TREE_UPDATE), userVO.getName(), XbbRefTypeEnum.CONTRACT.getName(), newPaasFormDataEntity.getSerialNo(), opObjectName);
                    List<DetailLogComplexPojo> detailArr = logHelp.detailLog4ContractPerformance(contractPerformanceLogList);
                    mongoLogHelp.buildLog4ComplexDetailArr(detailArr, userVO.getCorpid(), userVO.getUserId(), userVO.getName(), operateModuleTypeEnum, operateTypeEnum, contractId.toString(), opObjectName, memo, "");
                }
            } catch (Exception e) {
                LOG.error("保存合同更新合同业绩分配", e);
            }
            try {
                if (Objects.nonNull(newCustomerId) && newCustomerId > 0) {
                    // 更新客户状态和最后联系时间
                    JSONObject jsonObject = new JSONObject();
                    CustomerEntityExt customerEntityExt = customerModel.getByKey(newCustomerId, corpid);
                    //新建合同订单，取其创建时间更新影响客户最后跟进时间
                    Boolean flag = paasFormService.getSingleRuleList(corpid, RuleSettingEnum.NEW_CONTRACT_ORDER_CREATE_TIME.getConfig(), customerEntityExt.getFormId());
                    //只有新建合同才能更新最后跟进时间
                    if (isNew && flag) {
                        jsonObject.put(CustomerManagementEnum.LAST_CONNECT_TIME.getAttr(), DateTimeUtil.getInt());
                    }

                    Map<String, String> customerStatusMap = dataDictionaryModel.getDictionaryMap(DictionaryEnum.CUSTOMER_STATUS, corpid, null);
                    if (customerStatusMap.containsKey(CustomerTypeEnum.TRANSACTION_CUSTOMER.getCode())) {
                        String typeCode = (String) customerEntityExt.getData().get(CustomerManagementEnum.TYPE.getAttr());
                        //忠诚客户比成交客户优先级大(如果已经是忠诚客户就不变为成交客户)
                        if (Objects.isNull(typeCode) ||
                                (!Objects.equals(typeCode, CustomerTypeEnum.TRANSACTION_CUSTOMER.getCode())
                                        && !Objects.equals(typeCode, CustomerTypeEnum.LOYAL_CUSTOMERS.getCode()))) {
                            jsonObject.put(CustomerManagementEnum.TYPE.getAttr(), CustomerTypeEnum.TRANSACTION_CUSTOMER.getCode());
                        }
                    }
                    if (!jsonObject.isEmpty() && jsonObject.size() > 0) {
                        UpdateDataEntity updateDataEntity = ExplainUtil.getUpdateData(newCustomerId, jsonObject, corpid);
                        customerModel.updateBatch(Collections.singletonList(updateDataEntity), corpid);
                        //更新评分（合同信息变更，如果表单设置"更新规则"，可能修改客户的“最后更新时间”）
                        scoreCalculateService.asyncUpdateSingleDataScore(XbbRefTypeEnum.CUSTOMER_MANAGEMENT, corpid, updateDataEntity.getId(), null);
                    }
                }
            } catch (Exception e) {
                LOG.error("保存合同更新客户状态和最后联系时间", e);
            }

            try {
                Long signTime = newData.getLong(ContractEnum.SIGN_TIME.getAttr());
                publicSeniorRuleHelp.updateCustomerContractSignTime(signTime,corpid,newCustomerId,contractId);
            } catch (Exception e) {
                LOG.error("保存合同更新客户的合同签订时间", e);
            }

            try {
                // 如果合同关联了报价单，那么需要将关联的报价单状态改为已生成合同
                boolean changeQuotationStatus = (isNew || !Objects.equals(newQuotationId, oldQuotationId)) && (newQuotationId > 0);
                if (changeQuotationStatus) {
                    PaasFormDataEntityExt quotationEntityExt = quotationModel.getByKey(newQuotationId, corpid);
                    String oldStatus = FastJsonHelper.getStringOrDefaultFromFormData(quotationEntityExt.getData(), QuotationEnum.STATUS.getAttr(), "");
                    String newStatus = QuotationStatusEnum.CREATED.getCode();
                    if (!Objects.equals(oldStatus, newStatus)) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put(QuotationEnum.STATUS.getAttr(), newStatus);
                        UpdateDataEntity updateData = ExplainUtil.getUpdateData(newQuotationId, jsonObject, corpid);
                        quotationModel.updateBatch(Collections.singletonList(updateData), corpid);
                        newStatus = QuotationStatusEnum.CREATED.getName();
                        String name = FastJsonHelper.getStringOrDefaultFromFormData(quotationEntityExt.getData(), QuotationEnum.NAME.getAttr(), "");
                        QuotationEntityExt quotationEntity = new QuotationEntityExt();
                        BeanUtil.copyProperties(quotationEntityExt, quotationEntity);
                        List<String> pushUserList = pushHelper.getUserIdListByPushSetRule(corpid, PushTypeEnum.QUOTATION_STATUS_PUSH.getCode(), PushTypeEnum.QUOTATION_STATUS_PUSH.getSubCode(), saasFormSaveDTO.getLoginUser(), quotationEntity);
                        PushRefTypePojo.Options options = new PushRefTypePojo.Options(quotationEntityExt.getAppId(), quotationEntityExt.getMenuId(), quotationEntityExt.getFormId(), newQuotationId, Collections.singletonList(newQuotationId), SaasMarkEnum.SAAS.getCode(), saasFormSaveDTO.getBusinessType(), saasFormSaveDTO.getSubBusinessType());
                        MessageRabbitMqDTO messageRabbitMqDTO = new MessageRabbitMqDTO(corpid, pushUserList, Collections.singletonList(newQuotationId), 0, null, I18nMessageUtil.getMessage(QuotationPushConstant.QUOTATAION_CHANGE_TITLE), String.format(I18nMessageUtil.getMessage(QuotationPushConstant.QUOTATAION_CHANGE_CONTENT), saasFormSaveDTO.getLoginUserName(), name, newStatus), null, options);
                        baseProducer.sendMessage(PushTypeEnum.QUOTATION_STATUS_PUSH, messageRabbitMqDTO);
                    }
                }
            } catch (Exception e) {
                LOG.error("保存合同更新报价单状态", e);
            }

            try {
                boolean isLinkOpportunity = opportunityStageLogHelp.getIsLinkOpportunity(newPaasFormDataEntity.getFormId(), newPaasFormDataEntity.getCorpid());
                // 如果合同关联了机会，而且机会没有阶段或者机会有关联阶段但是不是成功阶段，那么需要将关联的机会阶段变为成功阶段
                if ((isNew || !Objects.equals(newOppId, oldOppId)) && (newOppId > 0) && isLinkOpportunity) {
                    JudgeSaleStageSuccessPojo judgeSaleStageSuccessPojo = opportunityStageLogHelp.judgeSaleStageSuccessWhenContract(corpid, newOppId);
                    if (judgeSaleStageSuccessPojo.getNeedChangeStage()) {
                        ChangeStageDTO changeStageDTO = new ChangeStageDTO();
                        BeanUtil.copyProperties(saasFormSaveDTO, changeStageDTO);
                        changeStageDTO.setOpportunity(judgeSaleStageSuccessPojo.getOpportunity());
                        changeStageDTO.setTargetStage(judgeSaleStageSuccessPojo.getTargetStage());
                        changeStageDTO.setDataId(newOppId);
                        changeStageDTO.setBusinessType(XbbRefTypeEnum.SALE_STAGE.getCode());
                        changeStageDTO.setNextCode(judgeSaleStageSuccessPojo.getTargetStage().getCode());
                        changeStageDTO.setFastUpdate(true);
                        changeStageDTO.setSourceStage(judgeSaleStageSuccessPojo.getSourceStage());
                        opportunityStageLogHelp.changeStage(changeStageDTO);
                    }
                }
            } catch (Exception e) {
                LOG.error("保存合同更新关联机会的阶段为赢单", e);
            }

            try {
                // 去除客户标签
                boolean customerSeniorModel = commonHelp.isCustomerPublicPoolSeniorModel(corpid);
                CustomerEntityExt customerEntity = customerModel.getByKey(newCustomerId, corpid);
                Long customerFormId  = customerEntity != null ? customerEntity.getFormId() : 0L;
                if (customerSeniorModel){
                    transferBackRuleScriptService.deletePublicSeniorTag(corpid,Collections.singletonList(newCustomerId),XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(),customerFormId);
                }else {
                    customerRuleService.deletePublicTag(corpid, Collections.singletonList(newCustomerId));
                }
            } catch (Exception e) {
                LOG.error("保存合同去除关联客户标签", e);
            }

            if (isNew) {
                //创建信息
                String creatorId = newPaasFormDataEntity.getCreatorId();
                UserEntity creatorUser = userModel.getByKey(creatorId, corpid);
                String creatorUserName = Objects.isNull(creatorUser) ? "" : creatorUser.getName();

                //是否关联报价单
                JSONObject data = newPaasFormDataEntity.getData();
                String quotationId = data.getString(ContractEnum.QUOTATION_ID.getAttr());
                //todo 可能会改
                if (Objects.equals(DistributorMarkEnum.DISTRIBUTOR.getCode(), saasFormSaveDTO.getDistributorMark())) {
                    if (quotationId != null) {
                        //新建合同关联报价单
                        String status = QuotationStatusEnum.CREATED.getName();
                        status = "\"" + status + "\"";
                        String contractName = data.getString(ContractEnum.NAME.getAttr());
                        contractName = contractName == null ? "" : ": " + contractName;
                        String communicateMemo = String.format(I18nMessageUtil.getMessage(LogMemoConstant.ADD_CONTRACT_RELATED_QUOTATION), creatorUser.getName()+" ", status) + contractName;
                        Integer subType = ProjectLogEnum.CONTRACT_QUOTATION.getSubType();
                        String log = I18nMessageUtil.getMessage(I18nStringConstant.CONTRACT_RELATED_QUOTATION_LOG);
                        projectLog(saasFormSaveDTO, creatorId, newCustomerId, communicateMemo, subType, log);
                    }
                    //新建合同
                    String operate = Objects.equals(saasFormSaveDTO.getIsImport(), 1) ? I18nMessageUtil.getMessage(I18nStringConstant.IMPORTED) : I18nMessageUtil.getMessage(I18nStringConstant.ADD);
                    String communicateMemo = creatorUser.getName() + operate + I18nMessageUtil.getMessage(CommonConstant.CONTRACT) + " :" + newPaasFormDataEntity.getSerialNo();
                    Integer subType = ProjectLogEnum.CONTRACT_CREATE.getSubType();
                    String log = I18nMessageUtil.getMessage(I18nStringConstant.CONTRACT_CREATE_LOG);
                    projectLog(saasFormSaveDTO, creatorId, newCustomerId, communicateMemo, subType, log);
                }else {
                    if (quotationId != null) {
                        //新建合同关联报价单
                        String status = QuotationStatusEnum.CREATED.getName();
                        status = "\"" + status + "\"";
                        String contractName = data.getString(ContractEnum.NAME.getAttr());
                        contractName = contractName == null ? "" : ": " + contractName;
                        String communicateMemo = String.format(I18nMessageUtil.getMessage(LogMemoConstant.ADD_CONTRACT_RELATED_QUOTATION), creatorUser.getName()+" ", status) + contractName;
                        transactionHelp.afterCommitOrCommitNow(p -> {
                            DynamicDTO dynamicDTO = dynamicDetailsHelper.initDynamicDTO(creatorId, communicateMemo, ProjectLogEnum.CONTRACT_CREATE.getSubType(), null, null, null, contractId);
                            dynamicDetailsHelper.insertDynamic(Collections.singletonList(dynamicDTO), corpid);
                        });
                    }

                    DynamicStrategy dynamicStrategy = dynamicStrategyFactory.getDynamicStrategy(ProjectLogEnum.CONTRACT_CREATE.getSubType());
                    transactionHelp.afterCommitOrCommitNow(p -> {
                        dynamicStrategy.save(DynamicSaveDTO.initDynamicSaveDTO(contractId, newPaasFormDataEntity.getFormId(), saasFormSaveDTO, creatorUser, null));
                    });
                }
                try {
                    if (!Objects.equals(saasFormSaveDTO.getIsImport(), 1)) {
                        // 合同创建消息推送
                        ContractEntityExt contractEntityExt = new ContractEntityExt();
                        BeanUtil.copyProperties(newPaasFormDataEntity, contractEntityExt);
                        List<String> pushUserList = pushHelper.getUserIdListByPushSetRule(corpid, PushTypeEnum.CONTRACT_ADD_PUSH.getCode(), PushTypeEnum.CONTRACT_ADD_PUSH.getSubCode(), creatorUser, contractEntityExt);
                        PushRefTypePojo.Options options = new PushRefTypePojo.Options(contractEntityExt.getAppId(), contractEntityExt.getMenuId(), contractEntityExt.getFormId(), contractId, Collections.singletonList(contractId), SaasMarkEnum.SAAS.getCode(), saasFormSaveDTO.getBusinessType(), saasFormSaveDTO.getSubBusinessType());
                        MessageRabbitMqDTO messageRabbitMqDTO = new MessageRabbitMqDTO(corpid, pushUserList, Collections.singletonList(contractId), 0, null, I18nMessageUtil.getMessage(ContractPushConstant.CONTRACT_ADD_TITLE), String.format(I18nMessageUtil.getMessage(ContractPushConstant.CONTRACT_ADD_CONTENT), creatorUser.getName(), contractEntityExt.getSerialNo()), null, options);
                        baseProducer.sendMessage(PushTypeEnum.CONTRACT_ADD_PUSH, messageRabbitMqDTO);
                    }
                } catch (Exception e) {
                    LOG.error("合同创建消息推送", e);
                }
            }

            try {
                if (!Objects.equals(saasFormSaveDTO.getIsImport(), 1)) {
                    // 合同状态变更消息推送
                    if (!Objects.equals(oldData.getString(ContractEnum.STATUS.getAttr()), newData.getString(ContractEnum.STATUS.getAttr()))) {
                        String oldStatus = dataDictionaryModel.getDictionaryValue(saasFormSaveDTO.getCorpid(), DictionaryEnum.CONTRACT_STATUS, oldData.getInteger(ContractEnum.STATUS.getAttr()));
                        String newStatus = dataDictionaryModel.getDictionaryValue(saasFormSaveDTO.getCorpid(), DictionaryEnum.CONTRACT_STATUS, newData.getInteger(ContractEnum.STATUS.getAttr()));
                        ContractEntityExt contractEntityExt = new ContractEntityExt();
                        BeanUtil.copyProperties(newPaasFormDataEntity, contractEntityExt);
                        List<String> pushUserList = pushHelper.getUserIdListByPushSetRule(corpid, PushTypeEnum.CONTRACT_CHANGE_PUSH.getCode(), PushTypeEnum.CONTRACT_CHANGE_PUSH.getSubCode(), saasFormSaveDTO.getLoginUser(), contractEntityExt);
                        PushRefTypePojo.Options options = new PushRefTypePojo.Options(contractEntityExt.getAppId(), contractEntityExt.getMenuId(), contractEntityExt.getFormId(), contractId, Collections.singletonList(contractId), SaasMarkEnum.SAAS.getCode(), saasFormSaveDTO.getBusinessType(), saasFormSaveDTO.getSubBusinessType());
                        String userName = "";
                        String commitUserId = saasFormSaveDTO.getCommitUserId();
                        if (StringUtils.isNotEmpty(commitUserId)) {
                            UserEntity userEntity = userModel.getByKey(commitUserId, corpid);
                            userName = userEntity.getName();
                        }else {
                            userName = saasFormSaveDTO.getLoginUserName();
                        }
                        MessageRabbitMqDTO messageRabbitMqDTO = new MessageRabbitMqDTO(corpid, pushUserList, Collections.singletonList(contractId), 0, null, I18nMessageUtil.getMessage(ContractPushConstant.CONTRACT_CHANGE_TITLE), String.format(I18nMessageUtil.getMessage(ContractPushConstant.CONTRACT_CHANGE_CONTENT), userName, oldStatus, newStatus), null, options);
                        baseProducer.sendMessage(PushTypeEnum.CONTRACT_CHANGE_PUSH, messageRabbitMqDTO);
                    }
                }
            } catch (Exception e) {
                LOG.error("合同保存时合同状态变更消息推送", e);
            }

            if (!Objects.equals(saasFormSaveDTO.getIsImport(), 1)) {
                try {
                    // 合同创建成功时未出库产品的待出库消息推送
                    if (Objects.equals(allOutbound, 0)) {
                        //推送给仓库主管 系统默认的仓库主管的roleId=22?
                        List<UserEntity> managers = userModel.getUsersByRoleId(corpid, Collections.singletonList(22));
                        List<String> userIdList = new ArrayList<>();
                        for (UserEntity manager : managers) {
                            userIdList.add(manager.getUserId());
                        }
                        PushRefTypePojo.Options options = new PushRefTypePojo.Options(newPaasFormDataEntity.getAppId(), newPaasFormDataEntity.getMenuId(), newPaasFormDataEntity.getFormId(), contractId, Collections.singletonList(contractId), SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CONTRACT.getCode(), XbbRefTypeEnum.CONTRACT.getCode());
                        MessageRabbitMqDTO messageRabbitMqDTO = new MessageRabbitMqDTO(saasFormSaveDTO.getCorpid(), userIdList, Collections.singletonList(contractId), 0, null, I18nMessageUtil.getMessage(OutstockPushConstant.OUTSTOCK_TITLE), String.format(I18nMessageUtil.getMessage(OutstockPushConstant.WAIT_OUTSTOCK_CONTENT), newPaasFormDataEntity.getSerialNo()), null, options);
                        baseProducer.sendMessage(PushTypeEnum.WAIT_OUTSTOCK_PUSH, messageRabbitMqDTO);
                        //编辑合同时，改变合同出库状态，更新待出库状态
                        //只有合同出库状态不为关闭才去更新
                        if (!Objects.equals(newData.get(ContractEnum.SHIP_STATUS.getAttr()),ShipStatusEnum.CLOSED.getCode())){
                            contractProductModel.updateWaitOutStatusById(Collections.singletonList(contractId),corpid, WaitOutStockStatusEnum.NORMAL.getCode(),WaitOutStockStatusEnum.NORMAL.getCode());
                        }
                    }else {
                        //编辑合同时，改变合同出库状态，更新待出库状态
                        contractProductModel.updateWaitOutStatusById(Collections.singletonList(contractId),corpid, WaitOutStockStatusEnum.UNUSUAL.getCode(),WaitOutStockStatusEnum.UNUSUAL.getCode());
                    }
                } catch (Exception e) {
                    LOG.error("合同创建成功时未出库产品的待出库消息推送", e);
                }


                try {
                    List<ContractProductEntity> contractProductEntityList = saasHelp.getContractProductWaitOutStock(corpid, Collections.singletonList(contractId));
                    JSONArray lackProductArray = saasHelp.getWaitPurchaseInfo(corpid, contractProductEntityList);
                    // 合同创建成功时缺货产品的待采购消息推送
                    if (Objects.equals(allOutbound, 0)) {
                        if (!lackProductArray.isEmpty()) {
                            // 推送给采购主管 系统默认的采购主管的roleId=21?
                            List<UserEntity> managers = userModel.getUsersByRoleId(corpid, Collections.singletonList(21));
                            List<String> userIdList = new ArrayList<>();
                            for (UserEntity manager : managers) {
                                userIdList.add(manager.getUserId());
                            }
                            PushRefTypePojo.Options options = new PushRefTypePojo.Options(newPaasFormDataEntity.getAppId(), newPaasFormDataEntity.getMenuId(),
                                    newPaasFormDataEntity.getFormId(), contractId, Collections.singletonList(contractId), SaasMarkEnum.SAAS.getCode(),
                                    XbbRefTypeEnum.CONTRACT.getCode(), saasFormSaveDTO.getSubBusinessType());
                            MessageRabbitMqDTO messageRabbitMqDTO = new MessageRabbitMqDTO(saasFormSaveDTO.getCorpid(), userIdList, Collections.singletonList(contractId), 0,
                                    null, I18nMessageUtil.getMessage(PurchasePushConstant.PURCHASE_TITLE), String.format(I18nMessageUtil.getMessage(PurchasePushConstant.WAIT_PURCHASE_CONTENT),
                                    newPaasFormDataEntity.getSerialNo()), null, options);
                            baseProducer.sendMessage(PushTypeEnum.WAIT_PURCHASE_PUSH, messageRabbitMqDTO);
                        }
                    }
                } catch (Exception e) {
                    LOG.error("合同创建成功时缺货产品的待采购消息推送", e);
                }
            }
        };
        threadPoolBeanConfig.addUpdateDelThreadPool().execute(runnable);
        return afterSavePojo;
    }



    public void projectLog(SaasFormSaveDTO saasFormSaveDTO, String creatorId, Long newCustomerId, String communicateMemo, Integer subType, String log) {
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        JSONObject newData = saasFormSaveDTO.getNewData();
        String corpid = newPaasFormDataEntity.getCorpid();
        Long contractId = newPaasFormDataEntity.getId();
        //拼装
        try {
            CustomerDynamicAddDTO customerDynamicAddDTO = new CustomerDynamicAddDTO();
            String customerName = newData.getString(ContractEnum.LINK_CUSTOMER_HIDE.getAttr());
            customerDynamicAddDTO.setCorpid(corpid);
            customerDynamicAddDTO.setUserId(creatorId);
            customerDynamicAddDTO.setCustomerId(newCustomerId);
            customerDynamicAddDTO.setCustomerName(customerName);
            customerDynamicAddDTO.setMemo(communicateMemo);
            customerDynamicAddDTO.setCommunicateBusinessText(newPaasFormDataEntity.getSerialNo());
            customerDynamicAddDTO.setCommunicateBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
            customerDynamicAddDTO.setDataId(contractId);
            customerDynamicAddDTO.setLogType(subType);
            customerDynamicAddDTO.setDistributorMark(saasFormSaveDTO.getDistributorMark());
            customerCommunicateService.addCustomerDynamic(customerDynamicAddDTO);
        } catch (Exception e) {
            LOG.error(log, e);
        }
    }

    public void saveProduct(JSONArray productArray, String corpid, PaasFormDataEntity paasFormDataEntity, Boolean isNew, Long formId, Integer distributorMark,Integer taskType) throws XbbException {
        Map<Long, ContractProductEntity> originProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> outstockNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> instockNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
//        // 编辑前后的总量Map
        Map<Long,Double> changeAfterNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long,Double> changeBeforeNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Long contractId = paasFormDataEntity.getDataId();
        List<Long> allProducIdList = new ArrayList<>();
        Boolean isOldData = false;
        if (!isNew) {
            List<ContractProductEntity> dbList = contractProductModel.getProductsByConId(contractId, corpid, Arrays.asList(DelEnum.NORMAL.getDel(), DelEnum.APPROVE_FAILED.getDel()));
            isOldData = judgeContractisOld(contractId, corpid);
            dbList.forEach(item -> {
                originProductMap.put(item.getId(), item);
                Double outstockNum = outstockNumMap.getOrDefault(item.getProductId(), 0D);
                outstockNumMap.put(item.getProductId(), Arith.add(outstockNum, item.getOutstockNum()));
                Double instockNum = instockNumMap.getOrDefault(item.getProductId(), 0D);
                instockNumMap.put(item.getProductId(), Arith.add(instockNum, item.getInstockNum()));
                if (changeBeforeNumMap.containsKey(item.getId())){
                    changeBeforeNumMap.put(item.getId(),Arith.add(changeBeforeNumMap.get(item.getId()),item.getProductNum()));
                }else {
                    changeBeforeNumMap.put(item.getId(),item.getProductNum());
                    allProducIdList.add(item.getProductId());
                }

            });
        }
        // 判断是否开启了缺货待生产显示
        FormConfigEntity configEntity = formConfigModel.getEntityByBusinessType(corpid, XbbRefTypeEnum.PRODUCTION_ORDER.getCode(), FormConfigEnum.PRODUCTION_ORDER_AUTO_UPDATE_NEW_CONTRACT.getConfig());
        //现在默认开启——凤娇定的  @date 2019/5/29 10:03
        boolean addContractShowProductFlag = true;
        String configValue = "";
        if (configEntity != null) {
            configValue = configEntity.getConfigValue();
        }
        if (configEntity != null && configValue != null && Objects.equals(configValue, "0")) {
            addContractShowProductFlag = false;
        }
        Map<Long, PromotionProductEntity> promotionMap = new HashMap<>();
        if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
            List<Long> productIds = new ArrayList<>();
            for (int i = 0; i < productArray.size(); i++) {
                JSONObject productArrayJSONObject = productArray.getJSONObject(i);
                productIds.add(productArrayJSONObject.getLongValue(SelectProductEnum.PRODUCT.getAttr()));
            }
            promotionMap = productHelp.getActivityList(corpid, paasFormDataEntity.getData().getLong(OrderEnum.LINK_DISTRIBUTOR.getAttr()), productIds);
        }
        List<ContractProductEntity> insertList = new ArrayList<>();
        List<ContractProductEntity> updaeList = new ArrayList<>();
        Map<Long, PaasFormDataEntityExt> productMap = formDataValidateProductHelp.product2saveMap(corpid, productArray);
        List<Long> changeProducIdList = new ArrayList<>();
        Set<Long> contractIdSet = new HashSet<>();
        contractIdSet.add(contractId);
        // 产品排序
        Integer sort = 0;
        for (int i = 0; i < productArray.size(); i++) {
            JSONObject jsonObject = productArray.getJSONObject(i);
            sort++;
            // 页面传入的产品id
            Long pageProductId = jsonObject.getLong(SelectProductEnum.PRODUCT.getAttr());
            Long businessProductId = jsonObject.getLong(BusinessConstant.PRODUCT_SUB_ID);
            boolean addProductFlag = formDataValidateProductHelp.addProductFlag(businessProductId);
            // 产品数量
            Double productNum = jsonObject.getDouble(ContractProductEnum.NUM.getAttr());
            Double price = jsonObject.getDouble(ContractProductEnum.SELLING_PRICE.getAttr());
            price = price != null ? price : 0;
            // 折扣：默认为100%
            Double discount = jsonObject.getDouble(ContractProductEnum.DISCOUNT.getAttr());
            discount = discount == null ? 100 : discount;
            // 备注
            String memo = jsonObject.getString(ContractProductEnum.MEMO.getAttr());
            if (Objects.isNull(jsonObject.get(ContractProductEnum.SUBTOTAL.getAttr())) || Objects.equals(jsonObject.getDouble(ContractProductEnum.SUBTOTAL.getAttr()), 0D)) {
                jsonObject.put(ContractProductEnum.SUBTOTAL.getAttr(), Arith.mul(price, productNum));
            }
            JSONObject productData = productMap.get(pageProductId).getData();
            ContractProductEntity contractProductEntity;
            if (addProductFlag) {
                contractProductEntity = new ContractProductEntity();
                contractProductEntity.setAddTime(DateTimeUtil.getInt());
                contractProductEntity.setUpdateTime(DateTimeUtil.getInt());
                contractProductEntity.setContractOutstockNum(0D);
                contractProductEntity.setInstockNum(0D);
                contractProductEntity.setPurchaseNum(productNum);
                insertList.add(contractProductEntity);
                jsonObject.put(ContractProductEnum.PROMOTION.getAttr(), BasicConstant.ZERO);
                if (promotionMap.containsKey(pageProductId)) {
                    jsonObject.put(ContractProductEnum.PROMOTION.getAttr(), BasicConstant.ONE);
                }
            } else if (originProductMap.containsKey(businessProductId)) {
                contractProductEntity = originProductMap.get(businessProductId);
                contractProductEntity.setUpdateTime(DateTimeUtil.getInt());
                if (Objects.equals(contractProductEntity.getDel(), DelEnum.APPROVE_FAILED.getDel())) {
                    contractProductEntity.setDel(DelEnum.NORMAL.getDel());
                }
                updaeList.add(contractProductEntity);
                originProductMap.remove(businessProductId);
                productData.put(ProductEnum.PRICE.getAttr(), contractProductEntity.getHistoricalPrice());
            } else {
                continue;
            }
            Integer enableMultiUnit = productData.getInteger(ProductEnum.ENABLE_MULTI_UNIT.getAttr());
            if (Objects.equals(enableMultiUnit, 1)) {
                formDataValidateProductHelp.setBusinessUnit(jsonObject, contractProductEntity);
                contractProductEntity.setProductUnit(jsonObject.getString(SelectProductEnum.UNIT.getAttr()));
            } else {
                contractProductEntity.setProductUnit(productData.getString(ProductEnum.UNIT.getAttr()));
                jsonObject.put(SelectProductEnum.UNIT.getAttr(), productData.getString(ProductEnum.UNIT.getAttr()));
            }
            contractProductEntity.setCorpid(corpid);
            contractProductEntity.setFormId(formId);
            contractProductEntity.setContractId(contractId);
            contractProductEntity.setParentId(productData.getLong(ProductEnum.PARENT_ID.getAttr()));
            contractProductEntity.setProductId(pageProductId);
            contractProductEntity.setProductNum(productNum);
            contractProductEntity.setProductName(productData.getString(ProductEnum.NAME.getAttr()));
            contractProductEntity.setProductNo(productData.getString(ProductEnum.PRODUCT_NO.getAttr()));
            JSONArray imgArr = FastJsonHelper.getJsonArrFromFormData(productData, ProductEnum.PRODUCT_IMGS.getAttr());
            if (Objects.isNull(imgArr) || imgArr.isEmpty()) {
                imgArr = FastJsonHelper.getJsonArrFromFormData(productData, ProductEnum.THUMBNAIL.getAttr());
            } else {
                imgArr = JSONArray.parseArray(JSON.toJSONString(imgArr.subList(0, 1)));
            }
            contractProductEntity.setProductThumbnail(imgArr.toJSONString());
            contractProductEntity.setPrice(price);
            contractProductEntity.setDiscount(discount);
            contractProductEntity.setSpecification(productData.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()));
            contractProductEntity.setMemo(memo);
            contractProductEntity.setBarcode(productData.getString(ProductEnum.BARCODE.getAttr()));
            contractProductEntity.setCost(jsonObject.getDouble(ContractEnum.ESTIMATE_COST.getSaasAttr()));
            contractProductEntity.setSort(sort);
            contractProductEntity.setType(ContractProductTypeEnum.CONTNRACT.getCode());
            contractProductEntity.setData(jsonObject);
            contractProductEntity.setHistoricalPrice(productData.getDouble(ProductEnum.PRICE.getAttr()));
            jsonObject.put(ContractProductEnum.PRICE.getAttr(), productData.getDouble(ProductEnum.PRICE.getAttr()));
            //老数据在编辑时维护一下出库数
            if (!isNew && isOldData) {
                if (outstockNumMap.containsKey(contractProductEntity.getProductId())) {
                    Double amendNum = outstockNumMap.get(contractProductEntity.getProductId());
                    Double amendInstockNum = instockNumMap.getOrDefault(contractProductEntity.getProductId(), 0D);
                    Double outstockNum = contractProductEntity.getProductNum() < amendNum ? contractProductEntity.getProductNum() : amendNum;
                    Double instockNum = contractProductEntity.getProductNum() < amendInstockNum ? contractProductEntity.getProductNum() : amendInstockNum;
                    contractProductEntity.setInstockNum(instockNum);
                    contractProductEntity.setOutstockNum(outstockNum);
                    contractProductEntity.setContractOutstockNum(outstockNum);
                    amendNum = Arith.sub(amendNum, outstockNum);
                    if (amendNum > 0) {
                        outstockNumMap.put(contractProductEntity.getProductId(), amendNum);
                    } else {
                        outstockNumMap.remove(contractProductEntity.getProductId());
                    }
                    amendInstockNum = Arith.sub(amendInstockNum, instockNum);
                    if (amendInstockNum > 0) {
                        instockNumMap.put(contractProductEntity.getProductId(), amendInstockNum);
                    } else {
                        instockNumMap.remove(contractProductEntity.getProductId());
                    }
                } else {
                    contractProductEntity.setInstockNum(0D);
                    contractProductEntity.setOutstockNum(0D);
                    contractProductEntity.setContractOutstockNum(0D);
                }
            }

            // -----------新建或编辑时增加了新的产品，判断该产品是显示在待生产还是待采购列表-----------------------
            //现在默认开启——凤娇定的  @date 2019/5/29 10:03
            if (addProductFlag && addContractShowProductFlag) {
                // 开启了缺货待生产显示，判断该产品有没有对应启用的不在审批中的bom单
                // TODO 批量查询
                if (bomBillService.hasEnabledBom(pageProductId, corpid)) {
                    contractProductEntity.setIsProduce(ContractProduceEnum.WAIT_PRODUCE.getCode());
                } else {
                    contractProductEntity.setIsProduce(ContractProduceEnum.WAIT_PURCHASE.getCode());
                }
            } else if (addProductFlag) {
                contractProductEntity.setIsProduce(ContractProduceEnum.WAIT_PURCHASE.getCode());
            }
            // -----------判断该产品是显示在待生产还是带采购列表-----------------------
        }
        if (CollectionUtils.isNotEmpty(insertList)) {
            contractProductModel.insertBatch(insertList);
        }
        Map<Long, Double> parentProductSalesMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> productSalesMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ContractProductEntity addProduct : insertList) {
            if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                Double parentNum = Arith.add(parentProductSalesMap.getOrDefault(addProduct.getParentId(), 0D), addProduct.getProductNum());
                parentProductSalesMap.put(addProduct.getParentId(), parentNum);
                Double num = Arith.add(productSalesMap.getOrDefault(addProduct.getProductId(), 0D), addProduct.getProductNum());
                productSalesMap.put(addProduct.getProductId(), num);
            }
        }
        // 经销商订单给父子产品增加销量
        if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
            List<UpdateDataEntity> updateList = new ArrayList<>();
            parentProductSalesMap.forEach((key, value) -> {
                JSONObject updateData = new JSONObject();
                updateData.put(ProductEnum.SALES.getAttr(), value);
                updateList.add(ExplainUtil.getUpdateData(key, updateData, corpid));
            });
            productSalesMap.forEach((key, value) -> {
                JSONObject updateData = new JSONObject();
                updateData.put(ProductEnum.SALES.getAttr(), value);
                updateList.add(ExplainUtil.getUpdateData(key, updateData, corpid));
            });
            if (CollectionsUtil.isNotEmpty(updateList)) {
                productModel.updateBatch(updateList, corpid);
            }
        }
        List<ContractProductEntity> nowContractProductList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(updaeList)) {
            for (ContractProductEntity updateProduct : updaeList) {
                nowContractProductList.add(updateProduct);
            }
            contractProductModel.updateMulti(updaeList, corpid);
        }
        List<ContractProductEntity> deleteList = new ArrayList<>();
        for (Map.Entry<Long, ContractProductEntity> entry : originProductMap.entrySet()) {
            ContractProductEntity delProduct = entry.getValue();
            delProduct.setDel(1);
            deleteList.add(delProduct);
        }
        // 处理采购结存及智能补货数据
        waitPurchaseService.formatContract(insertList,updaeList,deleteList,changeBeforeNumMap,corpid,taskType);

        // 处理合同缺货数量
        if (CollectionsUtil.isNotEmpty(updaeList)){
            contractHelp.handleWaitPurchaseNum(corpid,updaeList,contractIdSet);
        }
        for (ContractProductEntity updateProduct : updaeList) {
            contractProductModel.update(updateProduct);
            nowContractProductList.add(updateProduct);
        }
        contractProductModel.deleteBatch(deleteList, corpid, DelEnum.DELETE.getDel());
    }


    /**
     * 判断是否是老数据
     *
     * @param contractId
     * @param corpid
     * @return
     */
    private Boolean judgeContractisOld(Long contractId, String corpid) throws XbbException {
        Boolean isOld = false;
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery(OutstockEnum.getEsAttr4Keyword(OutstockEnum.TYPE), OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
        boolQueryBuilder.filter(termQuery(OutstockEnum.getEsAttr4Keyword(OutstockEnum.REF_ID), contractId));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_OUTSTOCK.getType()));
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.sort(new FieldSortBuilder("addTime").order(SortOrder.DESC));
        //分页
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 1, 1);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_OUTSTOCK.getIndex());
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        if (esEntities.getTotalElements() >= 1) {
            List<PaasFormDataEntityExt> content = esEntities.getContent();
            Long dataId = content.get(0).getDataId();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("outWarehouseId", dataId);
            param.put("pageSize", 1);
            List<OutstockProductEntity> entitys = outstockProductModel.findEntitys(param);
            for (OutstockProductEntity entity : entitys) {
                if (Objects.isNull(entity.getRefProductId())) {
                    isOld = true;
                    break;
                }
            }
        }
        return isOld;
    }

    @Override
    public void formatLinkAddExplain(HandlerExplainInLinkItemDTO handlerExplainInLinkItemDTO) throws XbbException {
        JSONObject linkFormData = handlerExplainInLinkItemDTO.getLinkFormData();
        Long linkDataId = handlerExplainInLinkItemDTO.getLinkDataId();
        String corpid = handlerExplainInLinkItemDTO.getCorpid();
        List<FieldAttrEntity> explainList = handlerExplainInLinkItemDTO.getExplainList();
        FieldAttrEntity linkCustomerField = new FieldAttrEntity();
        FieldAttrEntity linkContactField = new FieldAttrEntity();
        FieldAttrEntity linkOpportunityField = new FieldAttrEntity();
        FieldAttrEntity linkProductField = new FieldAttrEntity();
        FieldAttrEntity contractAmountField = new FieldAttrEntity();
        FieldAttrEntity productTotalField = new FieldAttrEntity();
        FieldAttrEntity discountField = new FieldAttrEntity();
        FieldAttrEntity otherExpenseField = new FieldAttrEntity();
        FieldAttrEntity discountAmountField = new FieldAttrEntity();
        FieldAttrEntity linkQuotationField = new FieldAttrEntity();
        for (FieldAttrEntity fieldAttrEntity : explainList) {
            if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.LINK_CUSTOMER.getAttr())) {
                linkCustomerField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.LINK_CONTACT.getAttr())) {
                linkContactField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.LINK_OPPORTUNITY.getAttr())) {
                linkOpportunityField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.PRODUCT.getAttr())) {
                linkProductField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.AMOUNT.getAttr())) {
                contractAmountField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.DISCOUNT.getAttr())) {
                discountField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.OTHER_EXPENSE.getAttr())) {
                otherExpenseField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.DISCOUNT_AMOUNT.getAttr())) {
                discountAmountField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.QUOTATION_ID.getAttr())) {
                linkQuotationField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), ContractEnum.PRODUCT_TOTAL.getAttr())) {
                productTotalField = fieldAttrEntity;
            }
        }
        if (Objects.equals(handlerExplainInLinkItemDTO.getFromGenerateBusiness(), 1)) {
            linkCustomerField.setEditable(0);
        }
        LinkProductRelyEntity linkProductRelyEntity;
        UserAndDepartmentGetDTO userAndDepartmentGetDTO = new UserAndDepartmentGetDTO();
        BeanUtil.copyProperties(handlerExplainInLinkItemDTO, userAndDepartmentGetDTO);
        UserAndDepartmentGetVO userAndDepartmentGetVO = userHelp.getUserDepartmentMap(userAndDepartmentGetDTO);
        Map<String, UserEntity> userMap = userAndDepartmentGetVO.getUserMap();
        Map<String, DepartmentEntity> departmentMap = userAndDepartmentGetVO.getDepartmentMap();
        RedundantTemplateTypeEnum redundantTemplateTypeEnum = RedundantTemplateTypeEnum.getByCode(handlerExplainInLinkItemDTO.getLinkBusinessType());
        DefaultAttrPoJo productDefaultAttr = linkProductField.getDefaultAttr() == null ? new DefaultAttrPoJo() : linkProductField.getDefaultAttr();
        String defaultType = productDefaultAttr.getDefaultType();
        // 源单解释
        Map<String, FieldAttrEntity> sourceExplainMap = null;
        // 当前表单解释
        Map<String, FieldAttrEntity> thisExplainMap = null;
        // 当前表单某个关联产品解释
        Map<String, FieldAttrEntity> subThisExplainMap = null;
        // 源单某个关联产品解释
        Map<String, FieldAttrEntity> subSourceExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // 当前表单某个关联产品子字段与源单某个关联产品对应关系
        Map<String, String> thisToTarget = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        switch (redundantTemplateTypeEnum) {
            case CUSTOMER:
                String customerName = FastJsonHelper.getStringOrDefaultFromFormData(linkFormData, CustomerManagementEnum.NAME.getAttr(), "");
                linkCustomerField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(linkDataId, customerName, corpid, XbbRefTypeEnum.CUSTOMER_MANAGEMENT, XbbRefTypeEnum.PREPAYMENT_BALANCE));
                break;
            case CONTACT:
                String contactName = FastJsonHelper.getStringOrDefaultFromFormData(linkFormData, ContactEnum.NAME.getAttr(), "");
                linkContactField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(linkDataId, contactName));
                JSONArray linkCustomerArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(linkFormData, ContactEnum.CUSTOMER_NAME.getAttr(), new JSONArray());
                linkCustomerField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(linkCustomerArr));
                break;
            case SALES_OPPORTUNITY:
                String opportunityNo = handlerExplainInLinkItemDTO.getPaasFormDataEntityExt().getSerialNo();
                linkOpportunityField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(linkDataId, opportunityNo, handlerExplainInLinkItemDTO.getSourceFormId()));
                JSONArray opportunityLinkCustomerArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(linkFormData, SalesOpportunityEnum.CUSTOMER_NAME.getAttr(), new JSONArray());
                linkCustomerField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(opportunityLinkCustomerArr));
                JSONArray contactArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(linkFormData, SalesOpportunityEnum.CONTACT_NAME.getAttr(), new JSONArray());
                linkContactField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(contactArr));
//                JSONObject productListObj = new JSONObject();
//                JSONArray productList = new JSONArray();
//                List<OpportunityProductEntity> opportunityProductEntityList = opportunityProductModel.getProductsByOppId(linkDataId, corpid);
//                for (OpportunityProductEntity opportunityProductEntity : opportunityProductEntityList) {
//                    JSONObject product = new JSONObject();
//                    product.put("parentId", opportunityProductEntity.getParentId());
//                    product.put(StringConstant.SAAS_LINK_BUSINESS_ID, opportunityProductEntity.getProductId());
//                    product.put(StringConstant.NUM,  opportunityProductEntity.getProductNum());
//                    product.put(RelativeProductEnum.SALE_DISCOUNT.getSaasAttr(), opportunityProductEntity.getDiscount());
//                    product.put(RelativeProductEnum.SALE_PRODUCT_PRICE.getSaasAttr(), opportunityProductEntity.getPrice());
//                    product.put("estimateCost",opportunityProductEntity.getCost());
//                    productList.add(product);
//                }
//                productListObj.put(StringConstant.PRODUCT_LIST, productList);
//                productListObj.put(BusinessConstant.OTHERS, new JSONObject());
                JSONObject contractDataList = new JSONObject();
//                contractDataList.put(ContractEnum.PRODUCT.getAttr(), productListObj);
//                SaasAnalysisDataDTO saasAnalysisDataDTO = new SaasAnalysisDataDTO();
//                saasAnalysisDataDTO.setForProcess(true);
//                saasAnalysisDataDTO.setBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
//                BeanUtil.copyProperties(handlerExplainInLinkItemDTO, saasAnalysisDataDTO);
                opportunityService.formatProduct4Show2(linkDataId, corpid, contractDataList, UpdateDataTypeEnum.LINK_ADD);
                JSONArray productArray = formDataValidateProductHelp.otherProductToAnother(contractDataList, linkProductField, ContractEnum.PRODUCT.getAttr(), SalesOpportunityEnum.PRODUCTS.getAttr(), corpid, handlerExplainInLinkItemDTO.getLoginUser(), handlerExplainInLinkItemDTO.getBusinessType(), handlerExplainInLinkItemDTO.getLinkBusinessType());
                // 关联产品自定义字段联动
                linkProductRelyEntity = linkProductRelyModel.getByAttrAndFormIdAndLinkFormId(corpid, ContractEnum.PRODUCT.getAttr(), handlerExplainInLinkItemDTO.getFormId(), handlerExplainInLinkItemDTO.getSourceFormId(), DelEnum.NORMAL.getDel());
                if (Objects.nonNull(defaultType) && Objects.equals(defaultType, DefaultTypeEnum.PRODUCT_DATA_RELY.getAlias()) && Objects.nonNull(linkProductRelyEntity)) {
                    // 源数据表单
                    PaasFormExplainEntity paasFormExplainEntity = proFormHelp.getExplainEntity(corpid, linkProductRelyEntity.getSourceFormId(), linkProductRelyEntity.getSourceBusinessType());
                    List<FieldAttrEntity> sourceExplainList = JSONArray.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
                    sourceExplainMap = ExplainUtil.getExplainMapByList(sourceExplainList, null);
                    // 当前表单解释
                    PaasFormExplainEntity displayFormExplain = proFormHelp.getExplainEntity(corpid, linkProductRelyEntity.getFormId(), linkProductRelyEntity.getBusinessType());
                    thisExplainMap = ExplainUtil.getExplainMap(displayFormExplain.getExplains(), null);
                    subThisExplainMap = businessProductService.getProductRelyMap(ContractEnum.PRODUCT.getAttr(), linkProductRelyEntity, sourceExplainMap, thisExplainMap, subSourceExplainMap, thisToTarget);
                    for (Object o : contractDataList.getJSONArray(SalesOpportunityEnum.PRODUCTS.getAttr())) {
                        JSONObject contractData = (JSONObject) o;
                        if (Objects.nonNull(contractData) && !thisToTarget.isEmpty()) {
                            ParseSingleRowDataDTO parseSingleRowDataDTO = new ParseSingleRowDataDTO();
                            parseSingleRowDataDTO.setDataJsonObject(contractData);
                            parseSingleRowDataDTO.setExplainMap(subSourceExplainMap);
                            parseSingleRowDataDTO.setUserMap(userMap);
                            parseSingleRowDataDTO.setDepartmentMap(departmentMap);
                            ParseSingleRowDataVO parseSingleRowDataVO = formDataUpdateGetAnalysisDataHelp.parseSingleRowData(parseSingleRowDataDTO);
                            if (Objects.isNull(parseSingleRowDataVO)) {
                                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.PARSE_SINGLE_ROW_DATA_FAILED);
                            }
                            JSONObject result = parseSingleRowDataVO.getResult();
                            JSONObject thisData = businessProductService.formatThisFormData(subThisExplainMap, thisToTarget, result);
                            for (int i = 0; i < productArray.size(); i++) {
                                JSONObject jsonObject = productArray.getJSONObject(i);
                                if (Objects.equals(jsonObject.getLongValue(BusinessConstant.RELY_PRODUCT_SUB_ID), contractData.getLongValue(BusinessConstant.RELY_PRODUCT_SUB_ID))) {
                                    jsonObject.putAll(thisData);
                                    break;
                                }
                            }
                        }
                    }
                }
                linkProductField.setDefaultAttr(saasUpdateHelp.setDefaultValue(productArray));
                linkProductField.getDefaultAttr().setDefaultType(defaultType);
                DefaultAttrPoJo defaultAttr = contractAmountField.getDefaultAttr();
                defaultAttr.setUseLinkValue(BasicConstant.ONE);
                defaultAttr.setLinkFormValue(getDoubleOrDefaultFromFormData(linkFormData, SalesOpportunityEnum.ESTIMATE_AMOUNT.getAttr(), 0D));
                contractAmountField.setDefaultAttr(defaultAttr);
                break;
            case QUOTATION:
                // 合同关联报价单设置默认值
                String quotationNo = handlerExplainInLinkItemDTO.getPaasFormDataEntityExt().getSerialNo();
                linkQuotationField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(linkDataId, quotationNo,handlerExplainInLinkItemDTO.getSourceFormId()));
                // 合同关联客户设置默认值
                JSONArray quotationLinkCustomerArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(linkFormData, QuotationEnum.CUSTOMER_ID.getAttr(), new JSONArray());
                linkCustomerField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(quotationLinkCustomerArr));
                // 合同关联联系人设置默认值
                JSONArray quotationContactArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(linkFormData, QuotationEnum.CONTACT_ID.getAttr(), new JSONArray());
                linkContactField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(quotationContactArr));
                // 合同关联机会设置默认值
                JSONArray quotationOppArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(linkFormData, QuotationEnum.OPPORTUNITY_ID.getAttr(), new JSONArray());
                linkOpportunityField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(quotationOppArr));
                //// 合同关联产品设置默认值
               /* JSONObject productListObject = new JSONObject();
                JSONArray productLists = new JSONArray();
                List<QuotationProductEntity> quotationProductEntityList = quotationProductModel.getProductsByQuoId(linkDataId, corpid);
                for (QuotationProductEntity quotationProductEntity : quotationProductEntityList) {
                    JSONObject product = new JSONObject();
                    product.put("parentId", quotationProductEntity.getParentId());
                    product.put(StringConstant.SAAS_LINK_BUSINESS_ID, quotationProductEntity.getProductId());
                    product.put(StringConstant.NUM,  quotationProductEntity.getProductNum());
                    product.put(RelativeProductEnum.SALE_DISCOUNT.getSaasAttr(), quotationProductEntity.getDiscount());
                    product.put(RelativeProductEnum.SALE_PRODUCT_PRICE.getSaasAttr(), quotationProductEntity.getPrice());
                    product.put("estimateCost",quotationProductEntity.getCost());
                    product.put(RelativeProductEnum.MEMO.getSaasAttr(), quotationProductEntity.getMemo());
                    productLists.add(product);
                }
                productListObject.put(StringConstant.PRODUCT_LIST, productLists);
                productListObject.put(BusinessConstant.OTHERS, new JSONObject());*/
                JSONObject quotationDataList = new JSONObject();
                /*quotationDataList.put(ContractEnum.PRODUCT.getAttr(), productListObject);
                SaasAnalysisDataDTO saasAnalysis = new SaasAnalysisDataDTO();
                saasAnalysis.setForProcess(true);
                saasAnalysis.setBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
                BeanUtil.copyProperties(handlerExplainInLinkItemDTO, saasAnalysis);
                formatProduct4Show(quotationDataList, saasAnalysis);
                JSONObject quotationProducts = quotationDataList.getJSONObject(ContractEnum.PRODUCT.getAttr());*/
                quotationService.formatProduct4Show2(linkDataId, corpid, quotationDataList, UpdateDataTypeEnum.LINK_ADD);
                JSONArray productArray1 = formDataValidateProductHelp.otherProductToAnother(quotationDataList, linkProductField, QuotationEnum.PRODUCT.getAttr(), QuotationEnum.PRODUCT.getAttr(), corpid, handlerExplainInLinkItemDTO.getLoginUser(), handlerExplainInLinkItemDTO.getBusinessType(), handlerExplainInLinkItemDTO.getLinkBusinessType());
                // 关联产品自定义字段联动
                linkProductRelyEntity = linkProductRelyModel.getByAttrAndFormIdAndLinkFormId(corpid, ContractEnum.PRODUCT.getAttr(), handlerExplainInLinkItemDTO.getFormId(), handlerExplainInLinkItemDTO.getSourceFormId(), DelEnum.NORMAL.getDel());
                if (Objects.nonNull(defaultType) && Objects.equals(defaultType, DefaultTypeEnum.PRODUCT_DATA_RELY.getAlias()) && Objects.nonNull(linkProductRelyEntity)) {
                    // 源数据表单
                    PaasFormExplainEntity paasFormExplainEntity = proFormHelp.getExplainEntity(corpid, linkProductRelyEntity.getSourceFormId(), linkProductRelyEntity.getSourceBusinessType());
                    List<FieldAttrEntity> sourceExplainList = JSONArray.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
                    sourceExplainMap = ExplainUtil.getExplainMapByList(sourceExplainList, null);
                    // 当前表单解释
                    PaasFormExplainEntity displayFormExplain = proFormHelp.getExplainEntity(corpid, linkProductRelyEntity.getFormId(), linkProductRelyEntity.getBusinessType());
                    thisExplainMap = ExplainUtil.getExplainMap(displayFormExplain.getExplains(), null);
                    subThisExplainMap = businessProductService.getProductRelyMap(ContractEnum.PRODUCT.getAttr(), linkProductRelyEntity, sourceExplainMap, thisExplainMap, subSourceExplainMap, thisToTarget);
                    for (Object o : quotationDataList.getJSONArray(QuotationEnum.PRODUCT.getAttr())) {
                        JSONObject quotationData = (JSONObject) o;
                        if (!thisToTarget.isEmpty()) {
                            ParseSingleRowDataDTO parseSingleRowDataDTO = new ParseSingleRowDataDTO();
                            parseSingleRowDataDTO.setDataJsonObject(quotationData);
                            parseSingleRowDataDTO.setExplainMap(subSourceExplainMap);
                            parseSingleRowDataDTO.setUserMap(userMap);
                            parseSingleRowDataDTO.setDepartmentMap(departmentMap);
                            ParseSingleRowDataVO parseSingleRowDataVO = formDataUpdateGetAnalysisDataHelp.parseSingleRowData(parseSingleRowDataDTO);
                            if (Objects.isNull(parseSingleRowDataVO)) {
                                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.PARSE_SINGLE_ROW_DATA_FAILED);
                            }
                            JSONObject result = parseSingleRowDataVO.getResult();
                            JSONObject thisData = businessProductService.formatThisFormData(subThisExplainMap, thisToTarget, result);
                            for (int i = 0; i < productArray1.size(); i++) {
                                JSONObject jsonObject = productArray1.getJSONObject(i);
                                if (Objects.equals(jsonObject.getLongValue(BusinessConstant.RELY_PRODUCT_SUB_ID), quotationData.getLongValue(BusinessConstant.RELY_PRODUCT_SUB_ID))) {
                                    jsonObject.putAll(thisData);
                                    break;
                                }
                            }
                        }
                    }
                }
                linkProductField.setDefaultAttr(saasUpdateHelp.setDefaultValue(productArray1));
                linkProductField.getDefaultAttr().setDefaultType(defaultType);
                // 合同金额设置默认值
                DefaultAttrPoJo quotationAmount = contractAmountField.getDefaultAttr();
                quotationAmount.setUseLinkValue(BasicConstant.ONE);
                quotationAmount.setLinkFormValue(getDoubleOrDefaultFromFormData(linkFormData, QuotationEnum.AMOUNT.getAttr(), 0D));
                contractAmountField.setDefaultAttr(quotationAmount);
                // 合同整单折扣设置默认值
                DefaultAttrPoJo quotationDiscount = discountField.getDefaultAttr();
                quotationDiscount.setUseLinkValue(BasicConstant.ONE);
                quotationDiscount.setLinkFormValue(getDoubleOrDefaultFromFormData(linkFormData, QuotationEnum.DISCOUNT.getAttr(), 1D));
                quotationDiscount.setDefaultValue(getDoubleOrDefaultFromFormData(linkFormData, QuotationEnum.DISCOUNT.getAttr(), 1D));
                discountField.setDefaultAttr(quotationDiscount);
                // 合同金额设置默认值
                DefaultAttrPoJo otherExpense = otherExpenseField.getDefaultAttr();
                otherExpense.setUseLinkValue(BasicConstant.ONE);
                otherExpense.setLinkFormValue(getDoubleOrDefaultFromFormData(linkFormData, QuotationEnum.OTHER_EXPENSE.getAttr(), 0D));
                otherExpenseField.setDefaultAttr(otherExpense);
                // 合同优惠金额设置默认值
                DefaultAttrPoJo discountAmount = discountAmountField.getDefaultAttr();
                discountAmount.setUseLinkValue(BasicConstant.ONE);
                discountAmount.setLinkFormValue(getDoubleOrDefaultFromFormData(linkFormData, QuotationEnum.DISCOUNT_AMOUNT.getAttr(), 0D));
                discountAmountField.setDefaultAttr(discountAmount);
                // 合同产品总计设置默认值
                DefaultAttrPoJo productTotal = productTotalField.getDefaultAttr();
                productTotal.setUseLinkValue(BasicConstant.ONE);
                productTotal.setLinkFormValue(getDoubleOrDefaultFromFormData(linkFormData, QuotationEnum.PRODUCT_TOTAL.getAttr(), 0D));
                productTotalField.setDefaultAttr(productTotal);
                break;
            default:
                throw new XbbException(TemplateErrorCodeEnum.API_ERROR_350006);
        }
    }

    @Override
    public void formatAddExplain(FormatExplainDTO formatExplainDTO) throws XbbException {
        contractExplainHandle.newHandleExplain(formatExplainDTO);
    }


    /**
     * 合同名称实时查重
     *
     * @param repeatCommonConditionDTO 查重共用条件
     * @param contractName             合同名称
     * @return com.xbongbong.paas.pojo.vo.FormDataRepeatVO
     * @throws XbbException 业务异常
     * @author feng.zheng
     * @date 2019/2/20 13:47
     */
    @Override
    public FormDataRepeatVO checkRepeatForContractName(RepeatCommonConditionDTO repeatCommonConditionDTO, String contractName) throws XbbException {
        // 合同名称判重
        FormDataRepeatVO formDataRepeatVO = new FormDataRepeatVO();
        if (contractName == null) {
            return formDataRepeatVO;
        }
        FormDataRepeatDTO formDataRepeatDTO = new FormDataRepeatDTO();
        BeanUtil.copyProperties(repeatCommonConditionDTO, formDataRepeatDTO);
        // 拼接条件，产品id
        ConditionsEntityExt conditionsEntityExt = new ConditionsEntityExt();
        conditionsEntityExt.setAttr(ContractEnum.NAME.getAttr());
        conditionsEntityExt.setSymbol(ConditionEnum.LIKE.getSymbol());
        conditionsEntityExt.setValue(Arrays.asList(contractName));
        conditionsEntityExt.setFieldType(FieldTypeEnum.TEXT.getType());
        formDataRepeatVO = saasSaveHelp.repeatOnlyByConditions(formDataRepeatDTO, Arrays.asList(conditionsEntityExt));
        return formDataRepeatVO;
    }

    /**
     * 获取合同剩余未出库产品信息及数量
     *
     * @param contractId     合同id
     * @param contractEntity 此参数可以为null ，用于更新关联出库单id，更安全
     * @param outstockIdList 此参数用来去除特定出库单，显示合同剩余出库产品，用于编辑出库单使用，可以为null
     * @return
     * @Param corpid 公司id
     */
    @Override
    public List<ContractProductEntity> getSurplusProduct(String corpid, Long contractId, ContractEntityExt contractEntity,
                                                         List<Long> outstockIdList) throws XbbException {

        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("contractId", contractId);
        param.put("del", 0);
        param.put("orderByStr", "CONVERT(product_name USING gbk) ASC");
        // 拿到合同的所有产品
        List<ContractProductEntity> contractProductEntityList = contractProductModel.findEntitys(param);

        List<Long> productIdIn = new ArrayList<>();
        for (ContractProductEntity entity : contractProductEntityList) {
            productIdIn.add(entity.getProductId());
        }
        Map<Long, ProductEntityExt> productMap = productModel.getProductMapByIdIn(corpid, productIdIn, null);
        for (ContractProductEntity entity : contractProductEntityList) {
            ProductEntityExt productEntity = productMap.get(entity.getProductId());
            if (productEntity != null) {
                entity.setBarcode(productEntity.getData().getString(ProductEnum.BARCODE.getAttr()));
            }
        }

        PaasFormDataEsListVO paasFormDataEsListVO = outstockService.getOutstockListFromEs(corpid, contractId, OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode(), 0);
        List<PaasFormDataEntityExt> paasFormDataESList = paasFormDataEsListVO.getPaasFormDataESList();
        List<Long> outstockIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        JSONArray outstockIdArray = new JSONArray();
        for (PaasFormDataEntityExt outstockEsEntity : paasFormDataESList) {
            outstockIdIn.add(outstockEsEntity.getDataId());
            outstockIdArray.add(outstockEsEntity.getDataId());
        }

        if (contractEntity != null) {
            // 根据已有的出库单重置数据，更安全
            JSONObject dataList = contractEntity.getData();
            dataList.put(ContractEnum.OUTSTOCK_ID.getAttr(), outstockIdArray);
            contractEntity.setData(dataList);
        }

        // 这里去掉特定出库单
        if (outstockIdList != null && !outstockIdList.isEmpty()) {
            outstockIdIn.removeAll(outstockIdList);
        }

        // 拿到所有的已出库产品
        List<OutstockProductEntity> outstockProductEntityList = new ArrayList<>();
        if (!outstockIdIn.isEmpty()) {
            param.clear();
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("outstockIdIn", outstockIdIn);
            outstockProductEntityList = outstockProductModel.findEntitys(param);
        }

        Collection<OutstockProductEntity> mergeOutstockProduct = outstockProductModel.mergeOutstockProduct(outstockProductEntityList);
        outstockProductEntityList.clear();
        outstockProductEntityList.addAll(mergeOutstockProduct);

        Collection<ContractProductEntity> mergeContractProduct = mergeContractProduct(contractProductEntityList);
        contractProductEntityList.clear();
        contractProductEntityList.addAll(mergeContractProduct);

        Iterator<ContractProductEntity> it = contractProductEntityList.iterator();
        while (it.hasNext()) {
            ContractProductEntity contractProductEntity = it.next();
            for (OutstockProductEntity outstockProductEntity : outstockProductEntityList) {
                if (contractProductEntity.getProductId().equals(outstockProductEntity.getProductId())) {
                    Double productNum = contractProductEntity.getProductNum() == null ? 0D : Math.abs(contractProductEntity.getProductNum());
                    double num = Arith.sub(productNum, outstockProductEntity.getProductNum());
                    if (num <= 0.00000001) {
                        //如果合同产品数量少于已出库数量，那么就将该产品去掉不显示.（出现这种情况是因为合同重新编辑数量变少了）
                        it.remove();
                        break;
                    } else {
                        contractProductEntity.setProductNum(num);
                    }
                }
            }
        }

        return contractProductEntityList;

    }

    /**
     * 获取合同剩余未出库产品信息及数量
     *
     * @param corpid
     * @param contractId
     * @param productList
     * @throws XbbException
     * @author long.rao
     * @date 2019-08-01 09:50
     */
    private JSONArray getSurplusContractProduct(String corpid, Long contractId, JSONArray productList) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        // 出库类型：1采购退货出库，2销售出库，3盘亏出库，4调拨出库，5其他出库，6装配出库, 7工单出库
        boolQueryBuilder.filter(termQuery("data." + OutstockEnum.TYPE.getAttr(), OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
        boolQueryBuilder.filter(termQuery("data." + OutstockEnum.REF_ID.getAttr(), contractId));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        List<String> fieldList = new ArrayList<>();
        fieldList.add(FieldTypeEnum.DATAID.getAlias());
        List<PaasFormDataEntityExt> outstockList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_OUTSTOCK, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        List<Long> outstockIdIn = new ArrayList<>();
        for (PaasFormDataEntityExt outstockEsEntity : outstockList) {
            outstockIdIn.add(outstockEsEntity.getDataId());
        }

        // 拿到所有的已出库产品
        List<OutstockProductEntity> outstockProductEntityList = new ArrayList<>();
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (!outstockIdIn.isEmpty()) {
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("outstockIdIn", outstockIdIn);
            outstockProductEntityList = outstockProductModel.findEntitys(param);
        }

        Collection<OutstockProductEntity> mergeOutstockProduct = outstockProductModel.mergeOutstockProduct(outstockProductEntityList);
        outstockProductEntityList.clear();
        outstockProductEntityList.addAll(mergeOutstockProduct);

        JSONArray mergeProduct = mergeContractProduct(productList);

        Iterator<Object> it = mergeProduct.iterator();
        while (it.hasNext()) {
            JSONObject contractProductEntity = (JSONObject) it.next();
            Long productId = FastJsonHelper.getLongFromFormData(contractProductEntity, SelectProductEnum.PRODUCT.getAttr());
            Double productNum = getDoubleOrDefaultFromFormData(contractProductEntity, SelectProductEnum.NUM.getAttr(), 0D);
            for (OutstockProductEntity outstockProductEntity : outstockProductEntityList) {
                if (productId.equals(outstockProductEntity.getProductId())) {
                    double num = Arith.sub(productNum, outstockProductEntity.getProductNum());
                    if (num <= 0.00000001) {
                        //如果合同产品数量少于已出库数量，那么就将该产品去掉不显示.（出现这种情况是因为合同重新编辑数量变少了）
                        it.remove();
                        break;
                    } else {
                        contractProductEntity.put(SelectProductEnum.NUM.getAttr(), num);
                    }
                }
            }
        }
        return mergeProduct;
    }

    /**
     * 获取红冲合同剩余未入库产品信息及数量
     *
     * @param corpid            公司Id
     * @param contractId        合同id
     * @param contractEntityExt 此参数可以为null ，用于更新关联入库单id，更安全
     * @param instockIdList     此参数用来去除特定入库单，显示红冲合同剩余入库产品，用于编辑入库单使用，可以为null
     * @return List<ContractProductEntity>
     * @date 2019/2/25 10:09
     */
    @Override
    public List<ContractProductEntity> getRedSurplusProduct(String corpid, Long contractId, ContractEntityExt contractEntityExt,
                                                            List<Long> instockIdList) throws XbbException {
        Map<String, Object> param = new HashMap<>(16);
        param.put("corpid", corpid);
        param.put("contractId", contractId);
        param.put("del", 0);
        // 拿到红冲合同的所有产品
        List<ContractProductEntity> contractProductEntityList = contractProductModel.findEntitys(param);

        List<Long> productIdIn = new ArrayList<>();
        for (ContractProductEntity entity : contractProductEntityList) {
            productIdIn.add(entity.getProductId());
        }
        Map<Long, ProductEntityExt> productMap = productModel.getProductMapByIdIn(corpid, productIdIn, null);

        // 由于退货入库的关联单据是退货退款单，根据红冲合同去查询退货退款单
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        //解析查询条件
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery(RefundEnum.getEsAttr4Keyword(RefundEnum.RED_CONTRACT_ID), contractId));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_REFUND.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_REFUND.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 1, 1);
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<RefundEntityExt> refundEntityExtList = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, RefundEntityExt.class);
        Long refundId;
        if (refundEntityExtList.getContent().size() == 0) {
            refundId = -1L;
        } else {
            refundId = refundEntityExtList.getContent().get(0).getDataId();
        }

        PaasFormDataEsListVO paasFormDataEsListVO = instockService.getInstockListFromEs(corpid, refundId, 2);
        List<PaasFormDataEntityExt> paasFormDataESList = paasFormDataEsListVO.getPaasFormDataESList();
        // 拿到该采购合同对应的所有入库单id
        List<Long> instockIds = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt instockEsEntity : paasFormDataESList) {
            instockIds.add(instockEsEntity.getDataId());
        }
        if (contractEntityExt != null) {
            JSONObject dataList = contractEntityExt.getData();
            dataList.put(ContractEnum.INSTOCK_ID.getAttr(), instockIds);
            contractEntityExt.setData(dataList);
        }
        // 这里去掉特定入库单
        instockModel.removeInstock(instockIdList, instockIds);

        instockIds.add(-1L);

        param.clear();
        param.put("corpid", corpid);
        param.put("del", 0);
        param.put("instockIdIn", instockIds);
        // 拿到所有的已入库产品
        List<InstockProductEntity> instockProductEntityList = instockProductModel.findEntitys(param);

        Iterator<ContractProductEntity> it = contractProductEntityList.iterator();
        while (it.hasNext()) {
            ContractProductEntity contractProductEntity = it.next();
            ProductEntityExt product = productMap.get(contractProductEntity.getProductId());
            if (product != null) {
                JSONObject dataList = product.getData();
                contractProductEntity.setBarcode(dataList.getString(ProductEnum.BARCODE.getAttr()));
            }
            for (InstockProductEntity instockProductEntity : instockProductEntityList) {
                if (contractProductEntity.getProductId().equals(instockProductEntity.getProductId())) {
                    // 红冲合同产品数量为负
                    Double contractProductNum = contractProductEntity.getProductNum() == null ? 0D : Math.abs(contractProductEntity.getProductNum());
                    BigDecimal b1 = new BigDecimal(contractProductNum.toString());
                    BigDecimal b2 = new BigDecimal(instockProductEntity.getProductNum().toString());
                    double num = b1.subtract(b2).doubleValue();
                    if (num <= 0.00000001) {
                        // 如果红冲合同产品数量少于已入库数量，那么就将该产品去掉不显示.
                        it.remove();
                        break;
                    } else {
                        // 红冲合同产品数量为负
                        contractProductEntity.setProductNum(-num);
                    }
                }
            }
        }
        return contractProductEntityList;
    }

    /**
     * 获取红冲合同产品，并且计算入库成本
     *
     * @param corpid     公司id
     * @param contractId 红冲合同id
     * @return 红冲合同关联的出库产品
     * 修改时间：2018-04-26 15:54
     * @author lcx
     * @since 1.0
     */
    @Override
    public List<ContractProductEntity> getRedContractOutstockProducts(String corpid, Long contractId) throws XbbException {
        // 获取红冲合同剩余未入库产品
        List<ContractProductEntity> contractProducts = getRedSurplusProduct(corpid, contractId, null, null);
        // 合并相同的剩余未入库产品
        Collection<ContractProductEntity> surplusProductList = mergeContractProduct(contractProducts);
        contractProducts.clear();
        contractProducts.addAll(surplusProductList);

        // 开启产品成本字段,需要去出库产品表中拿成本
//        if (paasFormExplainService.checkProductCostVisibleByAttr(corpid)) {
//            // 获取红冲合同的原合同
//            Map<String, Object> param = new HashMap<>();
//            param.put("corpid", corpid);
//            param.put("del", 0);
//            param.put("redContractId", contractId);
//            List<RefundEntity> refundEntityList = refundModel.findEntitys(param);
//
//            //获取原合同的关联出库单ids
//            List<OutstockProductEntity> outstockProductList = new ArrayList<>();
//            param.clear();
//            param.put("corpid", corpid);
//            param.put("del", 0);
//            List<Integer> outstockIds = new ArrayList<>();
//            outstockIds.add(-1);
//            if (refundEntityList != null && refundEntityList.size() > 0) {
//                param.put("refId", refundEntityList.get(0).getContractId());
//                //去拿除了拒绝跟撤回的所有对限制产品数量有效的出库单
//                List<Integer> approveStatusIn = Arrays.asList(0, 1, 2, 4);
//                param.put("approveStatusIn", approveStatusIn);
//                List<OutstockEntity> outstockEntityList = outstockModel.findEntitys(param);
//                for (OutstockEntity outstockEntity : outstockEntityList) {
//                    outstockIds.add(outstockEntity.getId());
//                }
//                param.clear();
//                param.put("corpid", corpid);
//                param.put("del", 0);
//                param.put("outstockIdIn", outstockIds);
//                //获取原合同的关联出库产品，产品出库成本保存在出库产品表里
//                outstockProductList = outstockProductModel.findEntitys(param);
//            }
//            //合并相同产品 TODO 合并后备注处理
//            Map<Integer, OutstockProductEntity> outstockProductMap = new HashMap<>(16);
//            for (OutstockProductEntity entity : outstockProductList) {
//                Integer productId = entity.getProductId();
//                if (outstockProductMap.containsKey(productId)) {
//                    OutstockProductEntity mergeProduct = outstockProductMap.get(productId);
//                    Double mergeNum = Arith.add(mergeProduct.getProductNum(), entity.getProductNum());
//                    Double mergeCost = Arith.add(Arith.mul(mergeProduct.getProductNum(), mergeProduct.getCost()), Arith.mul(entity.getProductNum(), entity.getCost()));
//                    mergeProduct.setProductNum(mergeNum);
//                    if (Double.compare(mergeNum, 0) > 0) {
//                        mergeProduct.setCost(Arith.div(mergeCost, mergeNum));
//                    } else {
//                        mergeProduct.setCost(0D);
//                    }
//
//                    outstockProductMap.put(productId, mergeProduct);
//                } else {
//                    outstockProductMap.put(productId, entity);
//                }
//            }
//            outstockProductList.clear();
//            outstockProductList.addAll(outstockProductMap.values());
//
//            //产品出库成本作为退货入库产品成本价
//            for (ContractProductEntity contractProductEntity : contractProducts) {
//                for (OutstockProductEntity outstockProductEntity : outstockProductList) {
//                    if (Objects.equals(contractProductEntity.getProductId(), outstockProductEntity.getProductId())) {
//                        contractProductEntity.setCost(outstockProductEntity.getCost());
//                    }
//                }
//            }
//        }
//
        return contractProducts;
    }

    @Override
    @Deprecated
    public void formatProduct4Show(JSONObject dataList, SaasAnalysisDataDTO saasAnalysisDataDTO) throws XbbException {
        if (dataList == null || saasAnalysisDataDTO == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028);
        }
        String corpid = saasAnalysisDataDTO.getCorpid();
        Long contractId = saasAnalysisDataDTO.getDataId();
        JSONObject productFinalObj = new JSONObject();
        JSONArray productArray = new JSONArray();
        // 获取关联产品
        List<ContractProductEntity> conProductList = new ArrayList<>();
        if (saasAnalysisDataDTO.getForProcess()) {
            JSONArray productArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(FastJsonHelper.getJsonObjectOrDefaultFromFormData(dataList, ContractEnum.PRODUCT.getAttr(), new JSONObject()), StringConstant.PRODUCT_LIST, new JSONArray());
            Map<Long, ProductEntityExt> productMap = productModel.getProductMap(corpid, productArr);
            contractModel.getProductList(corpid, conProductList, productArr, productMap);
        } else {
            conProductList = contractProductModel.getProductsByConId(contractId, corpid);
        }

        if (conProductList == null || conProductList.size() == 0) {
            productFinalObj.put(StringConstant.PRODUCT_LIST, productArray);
            dataList.put(ContractEnum.PRODUCT.getAttr(), productFinalObj);
            return;
        }
        List<Long> oppIds = new ArrayList<>(conProductList.size());

        for (ContractProductEntity entity : conProductList) {
            oppIds.add(entity.getProductId());
        }
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        //param.put("del", 0);
        param.put("idIn", oppIds);
        List<ProductEntityExt> productEntityExts = productModel.findEntitys(param);
        if (productEntityExts == null || productEntityExts.size() == 0) {
            productFinalObj.put(StringConstant.PRODUCT_LIST, productArray);
            dataList.put(ContractEnum.PRODUCT.getAttr(), productFinalObj);
            return;
        }
        Map<Long, ProductEntityExt> map = new HashMap<>(productEntityExts.size());
        for (ProductEntityExt entityExt : productEntityExts) {
            map.put(entityExt.getId(), entityExt);
        }
        // 获取关联产品需要展示的字段
        BusinessProductEnum businessProductEnum = BusinessProductEnum.getByCode(XbbRefTypeEnum.CONTRACT.getCode());
        // 用于存放当前启用的产品模板解释的map
        Map<String, FieldAttrEntity> nowProductExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String, FieldAttrEntity> nowBomProductExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        explainHelp.getRelativeProductExplainsFromDB(corpid, nowProductExplainMap, nowBomProductExplainMap);

        Map<Integer, JSONObject> productMap = new TreeMap<>();
        if (businessProductEnum != BusinessProductEnum.UNKNOW) {
            List<RelativeProductEnum> businessProductEnumList = businessProductEnum.getList();
            // 产品解释
            PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByBusinessType(XbbRefTypeEnum.PRODUCT.getCode(), corpid);
            List<FieldAttrEntity> explainList = JSONObject.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);


            for (ContractProductEntity entity : conProductList) {
                JSONArray attrArray = new JSONArray();
                JSONObject productObj = new JSONObject();
                Long productId = entity.getProductId();
                if (map.containsKey(productId)) {
                    JSONObject productData = map.get(productId).getData();

                    Long parentId = productData.getLong(ProductEnum.PARENT_ID.getAttr());
                    PaasFormDataEntityExt parentDataEntity = productModel.getByKey(parentId, corpid);
                    boolean priceHide = false;
                    if (parentDataEntity != null) {
                        //协同团队
                        Map<Long, List<String>> mainUserMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        Map<Long, List<String>> coUserMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        userTeamService.getUserIdMap(Collections.singletonList(parentId), corpid, XbbRefTypeEnum.PRODUCT.getCode(), false, mainUserMap, coUserMap);
                        HandlerExplainDTO handlerExplainDTO = new HandlerExplainDTO();
                        BeanUtil.copyProperties(saasAnalysisDataDTO, handlerExplainDTO);
                        handlerExplainDTO.setCreatorId(parentDataEntity.getCreatorId());
                        handlerExplainDTO.setOwnerId(mainUserMap.getOrDefault(parentDataEntity.getDataId(), new ArrayList<>()));
                        handlerExplainDTO.setCoUserId(coUserMap.getOrDefault(parentDataEntity.getDataId(), new ArrayList<>()));
                        handlerExplainDTO.setExplainList(new ArrayList<>(nowProductExplainMap.values()));
                        priceHide = ExplainUtil.isFieldNeedHide(nowProductExplainMap.getOrDefault(ProductEnum.PRICE.getSaasAttr(), new FieldAttrEntity()), handlerExplainDTO);
                    }


                    Double price = entity.getPrice();
                    price = price == null ? 0 : price;
                    Double productNum = entity.getProductNum();
                    productNum = productNum == null ? 0 : productNum;
                    String memo = entity.getMemo();
                    Double discount = entity.getDiscount();
                    for (RelativeProductEnum productEnum : businessProductEnumList) {
                        String saasAttr;
                        if (Objects.equals(productEnum, PRODUCT_PRICE) || Objects.equals(productEnum, SALE_PRODUCT_PRICE) || Objects.equals(productEnum, SUBTOTAL)) {
                            saasAttr = ProductEnum.PRICE.getSaasAttr();
                        } else {
                            saasAttr = productEnum.getSaasAttr();
                        }
                        FieldAttrEntity fieldAttrEntity = null;
                        if (nowProductExplainMap.containsKey(saasAttr)) {
                            fieldAttrEntity = nowProductExplainMap.get(saasAttr);
                        }
                        switch (productEnum) {
                            case NAME:
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(NAME, entity.getProductName(), fieldAttrEntity));
                                break;
                            case PRODUCT_NO:
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(PRODUCT_NO, entity.getProductNo(), fieldAttrEntity));
                                break;
                            case BARCODE:
                                String barcode = productData.getString(ProductEnum.BARCODE.getAttr());
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(BARCODE, barcode, fieldAttrEntity));
                                break;
                            case SPECIFICATION:
                                // 格式化规格
                                String specification = specificationModel.joinSpecification(entity.getSpecification());
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(SPECIFICATION, specification, fieldAttrEntity));
                                break;
                            case PRODUCT_PRICE:
                                Double productPrice = productData.getDouble(ProductEnum.PRICE.getAttr());
                                productPrice = productPrice == null ? 0 : productPrice;
                                // TODO 需要重新处理
//                                boolean priceVisible = paasFormExplainService.checkVisibleByAttr(explainList, ProductEnum.PRICE.getAttr());
                                JSONObject priceObj = saasUpdateHelp.formatSelectProduct4Show(PRODUCT_PRICE, productPrice, fieldAttrEntity);
                                if (priceObj != null) {
                                    priceObj.put("hidePrice", priceHide);
                                    JsonHelperUtil.addToJSONArray(attrArray, priceObj);
                                }
                                break;
                            case SALE_DISCOUNT:
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(SALE_DISCOUNT, discount, fieldAttrEntity));
                                break;
                            case SALE_PRODUCT_PRICE:
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(SALE_PRODUCT_PRICE, price, fieldAttrEntity));
                                break;
                            case NUM:
                                if (Objects.isNull(fieldAttrEntity)) {
                                    fieldAttrEntity = new FieldAttrEntity();
                                }
                                FieldAttrEntity stockAttrEntity = nowProductExplainMap.get(ProductEnum.STOCK.getSaasAttr());
                                if (Objects.nonNull(stockAttrEntity)) {
                                    fieldAttrEntity.setAccuracy(stockAttrEntity.getAccuracy());
                                }
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(NUM, productNum, fieldAttrEntity));
                                break;
                            case MEMO:
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(MEMO, memo, fieldAttrEntity));
                                break;
                            case UNIT:
                                String productUnit = entity.getProductUnit();
                                String unitStr = productModel.getProductUnitTextByValue(productUnit, fieldAttrEntity);
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(UNIT, unitStr, fieldAttrEntity));
                                break;
                            case SUBTOTAL:
                                Double subTotal = price * productNum;
                                JsonHelperUtil.addToJSONArray(attrArray, saasUpdateHelp.formatSelectProduct4Show(SUBTOTAL, subTotal, fieldAttrEntity));
                                break;
                            case THUMBNAIL:
                                JSONArray thumbnailArray = productData.getJSONArray(ProductEnum.THUMBNAIL.getAttr());
                                if (Objects.isNull(thumbnailArray) || thumbnailArray.isEmpty()) {
                                    thumbnailArray = new JSONArray();
                                    thumbnailArray.add(StringConstant.DEFAULT_IMAGES);
                                }
                                productObj.put("thumbnail", thumbnailArray);
                                break;
                            default:
                                break;
                        }
                        productObj.put("result", attrArray);
                        productObj.put(StringConstant.SAAS_LINK_BUSINESS_ID, entity.getProductId());
                        productObj.put(StringConstant.BUSINESS_PRODUCT_ID, entity.getId());
                        productObj.put("parentId", entity.getParentId());
                        productObj.put("estimateCost", entity.getCost());
                    }
                }
                productMap.put(entity.getSort(), productObj);
            }
            // 产品排序
            for (JSONObject jsonObject : productMap.values()) {
                productArray.add(jsonObject);
            }
            productFinalObj.put(StringConstant.PRODUCT_LIST, productArray);
            //JSONArray contractSurchargeArray = getContractSurchargeArray(corpid, contractId, saasAnalysisDataDTO.getFormId(), dataList);
            JSONObject others = new JSONObject();
            //others.put(StringConstant.SURCHARGE_LIST, contractSurchargeArray);
            productFinalObj.put(BusinessConstant.OTHERS, others);
            dataList.put(ContractEnum.PRODUCT.getAttr(), productFinalObj);
        }
    }

    @Override
    public PagingProductVO getPagingProduct(PagingProductDTO pagingProductDTO) throws XbbException {
        PagingProductVO pagingProductVO = new PagingProductVO();
        Map<String, Object> modelMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        modelMap.put(ParameterConstant.CORPID, pagingProductDTO.getCorpid());
        modelMap.put(ParameterConstant.CONTRACT_ID, pagingProductDTO.getDataId());
        modelMap.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        modelMap.put(ParameterConstant.PAGE, pagingProductDTO.getPage());
        modelMap.put(ParameterConstant.ORDER_BY_STR, BasicConstant.SORT);
        PageHelper pageHelper = PageHelperUtil.getPageHelper(modelMap, contractProductModel, pagingProductDTO.getPageSize());
        List<ContractProductEntity> contractProductList = (List<ContractProductEntity>) PageHelperUtil.getEntityList(modelMap, pageHelper, contractProductModel);
        if (pagingProductDTO.getUpdateDataTypeEnum().isDetail()) {
            DetailProductVO detailProductVO = formatProduct4DetailShow(contractProductList, pagingProductDTO);
            pagingProductVO.setProductDataArr(detailProductVO.getProductDataArr());
        }
        pagingProductVO.setPageHelper(pageHelper);
        return pagingProductVO;
    }

    public DetailProductVO formatProduct4DetailShow(List<ContractProductEntity> contractProductList, PagingProductDTO pagingProductDTO) throws XbbException {
        DetailProductVO detailProductVO = new DetailProductVO();
        JSONArray productArray = new JSONArray();
        if (CollectionsUtil.isEmpty(contractProductList)) {
            detailProductVO.setProductDataArr(new JSONArray());
            return detailProductVO;
        }
        Set<Long> productIds = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> contractId = new ArrayList<>();
        for (ContractProductEntity contractProductEntity : contractProductList) {
            productIds.add(contractProductEntity.getProductId());
            contractId.add(contractProductEntity.getContractId());
        }
        String corpid = pagingProductDTO.getCorpid();
        //根据合同关联产品表id,拿到采购合同关联产品表数据
        List<PurchaseProductEntity> purchaseProductEntityList = purchaseProductModel.getPurchaseByContract(corpid, contractId);
        Map<Long, PaasFormDataEntityExt> productMap = productService.getProductMapByIdIn(productIds, corpid, DelEnum.NORMAL);
        //区别新老数据行对应问题
        Map<Long, String> contractStatusMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        boolean enableDistributorApp = false;
        PaasAppEntity distributorApp = paasAppModel.getByAlias(XbbRefTypeEnum.DISTRIBUTOR.getAlias(), corpid);
        if (Objects.nonNull(distributorApp) && Objects.equals(distributorApp.getEnable(), BasicConstant.ONE)) {
            enableDistributorApp = true;
        }
        for (ContractProductEntity contractProductEntity : contractProductList) {
            Double rate = 1D;
            Double stock = 0D;
            Double productNum = contractProductEntity.getProductNum() == null ? 0D : contractProductEntity.getProductNum();
            Double outstockNum = contractProductEntity.getOutstockNum() == null ? 0D : contractProductEntity.getOutstockNum();
            Long productId = contractProductEntity.getProductId();
            PaasFormDataEntityExt paasFormDataEntityExt = productMap.get(productId);
            if (Objects.nonNull(paasFormDataEntityExt)) {
                stock = paasFormDataEntityExt.getData().getDouble(ProductEnum.STOCK.getAttr());
                stock = stock == null ? 0D : stock;
            }
            JSONObject json = new JSONObject();
            if (Objects.nonNull(contractProductEntity.getData())) {
                json = contractProductEntity.getData();
            }
            if (enableDistributorApp) {
                json.put(ContractProductEnum.PROMOTION.getSaasAttr(), json.get(ContractProductEnum.PROMOTION.getAttr()));
            } else {
                json.put(ContractProductEnum.PROMOTION.getSaasAttr(), BasicConstant.ZERO);
            }

            if (Objects.nonNull(contractProductEntity.getBusinessUnit())) {
                contractProductEntity.setProductNum(Arith.div(contractProductEntity.getProductNum(), contractProductEntity.getRate()));
                contractProductEntity.setProductUnit(contractProductEntity.getBusinessUnit().toString());
                rate = contractProductEntity.getRate();
                JSONObject priceData = contractProductEntity.getPriceData();
                if (Objects.nonNull(priceData) && !priceData.isEmpty()) {
                    contractProductEntity.setHistoricalPrice(priceData.getDouble(SelectProductEnum.PRICE.getAttr()));
                    contractProductEntity.setPrice(priceData.getDouble(SelectProductEnum.SELLING_PRICE.getAttr()));
                }
            }
            for (ContractProductEnum contractProductEnum : ContractProductEnum.values()) {
                if (!UpdateDataTypeEnum.DETAIL.formatShow(contractProductEnum.getShowType())) {
                    continue;
                }
                String attr = contractProductEnum.getAttr();
                switch (contractProductEnum) {
                    case PRODUCT:
                        json.put(attr, productId);
                        break;
                    case PRICE:
                        json.put(attr, contractProductEntity.getHistoricalPrice());
                        break;
                    case DISCOUNT:
                        json.put(attr, contractProductEntity.getDiscount());
                        break;
                    case SELLING_PRICE:
                        json.put(attr, contractProductEntity.getPrice());
                        break;
                    case NUM:
                        json.put(attr, contractProductEntity.getProductNum());
                        break;
                    case MEMO:
                        json.put(attr, contractProductEntity.getMemo());
                        break;
                    case UNIT:
                        json.put(attr, contractProductEntity.getProductUnit());
                        break;
                    case SUBTOTAL:
                        json.put(attr, Arith.mul(contractProductEntity.getPrice(), contractProductEntity.getProductNum()));
                        break;
                    case STOCK:
                        json.put(attr, stock);
                        break;
                    case OUTSTOCK_NUM:
                        json.put(attr, Arith.div(contractProductEntity.getContractOutstockNum(), rate));
                        break;
                    case SHIPMENTS_NUM:
                        json.put(attr, Arith.div(contractProductEntity.getShipmentsNum(), rate));
                        break;
                    case WAIT_OUTSTOCK_NUM:
                        Double num = Arith.sub(productNum, contractProductEntity.getContractOutstockNum());
                        Double waitOutstockNum = num >= 0 ? num : 0D;
                        json.put(attr, Arith.div(waitOutstockNum, rate));
                        break;
                    case RETURN_NUM:
                        json.put(attr, Arith.div(contractProductEntity.getInstockNum(), rate));
                        break;
                    case OUT_OF_STOCK:
                        Double purchaseNum = BasicConstant.ZERO_DOUBLE;
                        if (CollectionsUtil.isNotEmpty(purchaseProductEntityList)){
                            for (PurchaseProductEntity purchaseProductEntity : purchaseProductEntityList){
                                if (Objects.equals(purchaseProductEntity.getContractProductId(), contractProductEntity.getId())){
                                    purchaseNum = Arith.add(purchaseNum, purchaseProductEntity.getProductNum());
                                }
                            }
                        }
                        json.put(attr, purchaseNum);
                        break;
                    case CONTRACT_STATUS:
                        //老数据没有历史单价字段
                        if (Objects.isNull(contractProductEntity.getHistoricalPrice())) {
                            if (contractStatusMap.containsKey(productId)) {
                                json.put(attr, contractStatusMap.get(productId));
                            } else {
                                if (outstockNum > 0) {
                                    if (Double.compare(productNum, outstockNum) <= 0) {
                                        json.put(attr, I18nMessageUtil.getMessage(I18nStringConstant.OUT_STOCK));
                                    } else {
                                        json.put(attr, I18nMessageUtil.getMessage(I18nStringConstant.PARTIAL_DELIVERY));
                                    }
                                } else {
                                    json.put(attr, InOutStockStatusEnum.UN_OUTSTOCK.getName());
                                }
                                contractStatusMap.put(productId, json.getString(attr));
                            }
                        } else {
                            //新数据行对应走这边
                            if (outstockNum > 0) {
                                if (Double.compare(productNum, outstockNum) <= 0) {
                                    json.put(attr, I18nMessageUtil.getMessage(I18nStringConstant.OUT_STOCK));
                                } else {
                                    json.put(attr, I18nMessageUtil.getMessage(I18nStringConstant.PARTIAL_DELIVERY));
                                }
                            } else {
                                json.put(attr, InOutStockStatusEnum.UN_OUTSTOCK.getName());
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
            json.put(BusinessConstant.PRODUCT_SUB_ID, contractProductEntity.getId());
            json.put(BusinessConstant.PARENT_ID, contractProductEntity.getParentId());
            productArray.add(json);
        }
        detailProductVO.setProductDataArr(productArray);
        return detailProductVO;
    }

    @Override
    public void formatProduct4Show3(List<ContractProductEntity> contractProductEntityList, JSONObject productData, UpdateDataTypeEnum updateDataTypeEnum) throws XbbException {
        contractModel.formatProduct4Show3(contractProductEntityList, productData, updateDataTypeEnum);
    }

    @Override
    public JSONObject formatSingleProduct(ContractProductEntity entity) {
        return contractModel.formatSingleProduct(entity);
    }

    @Override
    public JSONObject formatSingleRefundProduct(ContractProductEntity entity) {
       return contractModel.formatSingleRefundProduct(entity);
    }

    @Override
    public ChangeDocumentStatusSaveVO changeDocumentStatus(ChangeDocumentStatusSaveDTO changeDocumentStatusSaveDTO) throws XbbException {
        ChangeDocumentStatusSaveVO changeDocumentStatusSaveVO = new ChangeDocumentStatusSaveVO();
        try {
            Boolean isOpen = changeDocumentStatusSaveDTO.getIsOpen();
            String corpid = changeDocumentStatusSaveDTO.getCorpid();
            Long contractId = changeDocumentStatusSaveDTO.getDataId();
            Integer businessType = changeDocumentStatusSaveDTO.getBusinessType();
            ContractEntityExt contractEntityExt = contractModel.getByKey(contractId, corpid);
            if (Objects.isNull(contractEntityExt)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, MessageConstant.FORM_DATA_NOT_EXIST);
            }
            JSONObject contractData = contractEntityExt.getData();
            // 查询出合同产品数据
            List<ContractProductEntity> contractProductEntityList = contractProductModel.getProductsByConId(contractId, corpid);
            // 合同产品数量Map
            Map<Long,Double> productNumMap = new HashMap<>();
            // 已出库数量Map
            Map<Long,Double> outStockNumMap = new HashMap<>();
            // 未出库数量Map
            Map<Long,Double> waitOutStockNumMap = new HashMap<>();
            List<Long> productIdList = new ArrayList<>();
            //封装数量
            if (CollectionsUtil.isNotEmpty(contractProductEntityList)){
                for (ContractProductEntity contractProductEntity : contractProductEntityList) {
                    Long productId = contractProductEntity.getProductId();
                    Double productNum = contractProductEntity.getProductNum();
                    Integer isProduce = contractProductEntity.getIsProduce();
                    // 进入待生产的不计算只能补货
                    if (Objects.equals(1,isProduce)){
                        continue;
                    }
                    productIdList.add(productId);
                    if (productNumMap.containsKey(productId)){
                        productNumMap.put(productId,Arith.add(productNumMap.get(productId),productNum));
                    }else {
                        productNumMap.put(productId,productNum);
                    }
                    if (outStockNumMap.containsKey(productId)){
                        outStockNumMap.put(productId,Arith.add(outStockNumMap.get(productId),contractProductEntity.getOutstockNum()));
                    }else {
                        outStockNumMap.put(productId,contractProductEntity.getOutstockNum());
                    }
                }
            }
            for (Map.Entry<Long, Double> entry : productNumMap.entrySet()) {
                Long productId = entry.getKey();
                Double productNum = entry.getValue();
                Double outStockNum = outStockNumMap.getOrDefault(productId, 0D);
                waitOutStockNumMap.put(productId,Arith.sub(productNum,outStockNum));
            }
            // 查询出智能补货数据 更新智能补货数量
            List<SmartReplenishmentEntity> smartReplenishmentEntityList = smartReplenishmentModel.getByProductList(corpid, productIdList);
            //如果是开启合同
            if (isOpen) {
                Integer allOutBound = contractData.getInteger(ContractEnum.ALL_OUT_BOUND.getAttr());
                //如果全部出库则直接发货设置成全部出库
                if (Objects.equals(allOutBound, AllOutBoundEnum.ALL_OUT.getCode())) {
                    contractData.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.SHIPPED.getCode());
                } else {
                    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
                    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
                    boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                    boolQueryBuilder.filter(termQuery(OutstockEnum.getEsAttr4Keyword(OutstockEnum.TYPE), OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
                    boolQueryBuilder.filter(termQuery(OutstockEnum.getEsAttr4Keyword(OutstockEnum.REF_ID), contractId));
                    boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
                    boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_OUTSTOCK.getType()));
                    sourceBuilder.query(boolQueryBuilder);
                    PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 1, 1);
                    SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_OUTSTOCK.getIndex());
                    searchRequest.source(sourceBuilder);
                    XbbAggregatedPage<PaasFormDataEntityExt> entities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);

                    if (entities.getTotalElements() < 1) {
                        contractData.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.UNSHIPPED.getCode());
                    } else {
                        contractData.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.DELIVERED.getCode());
                    }
                }
                // 查询出智能补货数据 更新智能补货数量
                if (CollectionsUtil.isNotEmpty(smartReplenishmentEntityList)){
                    for (SmartReplenishmentEntity smartReplenishmentEntity : smartReplenishmentEntityList) {
                        Long productId = smartReplenishmentEntity.getProductId();
                        // 当前改产品未出库数量
                        Double waitOutstockNum = smartReplenishmentEntity.getWaitOutstockNum();
                        // 开启合同时未出库数量 = 当前改产品未出库数量 + (当前合同改产品未出库数量)
                        Double waitOutNum = waitOutStockNumMap.getOrDefault(productId, 0D);
                        smartReplenishmentEntity.setWaitOutstockNum(Arith.add(waitOutNum,waitOutstockNum));
                    }
                }
            } else {
                contractData.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.CLOSED.getCode());
                // 查询出智能补货数据 更新智能补货数量
                if (CollectionsUtil.isNotEmpty(smartReplenishmentEntityList)){
                    for (SmartReplenishmentEntity smartReplenishmentEntity : smartReplenishmentEntityList) {
                        Long productId = smartReplenishmentEntity.getProductId();
                        // 当前改产品未出库数量
                        Double waitOutstockNum = smartReplenishmentEntity.getWaitOutstockNum();
                        // 关闭合同时未出库数量 = 当前改产品未出库数量 - (当前合同改产品未出库数量)
                        Double waitOutNum = waitOutStockNumMap.getOrDefault(productId, 0D);
                        smartReplenishmentEntity.setWaitOutstockNum(Arith.sub(waitOutstockNum,waitOutNum));
                    }
                }
            }
            if (CollectionsUtil.isNotEmpty(smartReplenishmentEntityList)){
                smartReplenishmentModel.updateBatch(smartReplenishmentEntityList,corpid);
            }
            contractModel.update(contractEntityExt);
            //同步关闭状态到关联产品表
            if (!isOpen) {
                contractProductModel.updateWaitOutStatusById(Collections.singletonList(contractId), corpid, WaitOutStockStatusEnum.UNUSUAL.getCode(),BasicConstant.THREE);
            } else if (isOpen) {
                contractProductModel.updateWaitOutStatusById(Collections.singletonList(contractId), corpid, WaitOutStockStatusEnum.NORMAL.getCode(),WaitOutStockStatusEnum.NORMAL.getCode());
            }
            //添加关闭和开启合同的按钮
            String shipStatus = FastJsonHelper.getStringOrDefaultFromFormData(contractData, ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.UNSHIPPED.getCode());
            //如果全部出库，则没有开启关闭按钮
            if (!Objects.equals(shipStatus, ShipStatusEnum.SHIPPED.getCode())) {
                ButtonPojo buttonPojo = new ButtonPojo();
                if (Objects.equals(shipStatus, ShipStatusEnum.CLOSED.getCode()) && changeDocumentStatusSaveDTO.getLoginUser().getPermSet().contains(EnableButtonEnum.CONTRACT_ENABLE.getPermission())) {
                    buttonPojo.setAttr("contract");
                    buttonPojo.setValue(OperateTypeEnum.ENABLE.getName());
                    buttonPojo.setLinkBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
                    List<ButtonPojo> topRightButton = new ArrayList<>();
                    topRightButton.add(buttonPojo);
                    changeDocumentStatusSaveVO.setTopRightButton(topRightButton);
                } else if (!Objects.equals(shipStatus, ShipStatusEnum.CLOSED.getCode()) && changeDocumentStatusSaveDTO.getLoginUser().getPermSet().contains(EnableButtonEnum.CONTRACT_UNABLE.getPermission())) {
                    buttonPojo.setAttr("contract");
                    buttonPojo.setValue(OperateTypeEnum.UNABLE.getName());
                    buttonPojo.setLinkBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
                    List<ButtonPojo> topRightButton = new ArrayList<>();
                    topRightButton.add(buttonPojo);
                    changeDocumentStatusSaveVO.setTopRightButton(topRightButton);
                }
            }
            // 刷新页面saas关联新建工作流
            PaasFormDataEntityExt paasFormDataEntityExt = new PaasFormDataEntityExt();
            BeanUtil.copyProperties(contractEntityExt, paasFormDataEntityExt);
            if (Objects.equals(PlatFormEnum.DINGTALK.getValue(), changeDocumentStatusSaveDTO.getPlatform())) {
                // 设置移动端详情右上角操作
                FormDataGetVO formDataGetVO = new FormDataGetVO();
                mobileDetailService.handelMobileWorkFlow(changeDocumentStatusSaveDTO.getLoginUser(), businessType, formDataGetVO, paasFormDataEntityExt);
                changeDocumentStatusSaveVO.setTopPermissions(formDataGetVO.getTopPermissions());
            } else {
                List<WorkFlowPojo> workFlowPojoList = new ArrayList<>();
                proDetailHandlerHelp.handelDetailWorkFlow(businessType, paasFormDataEntityExt, workFlowPojoList, changeDocumentStatusSaveDTO.getLoginUser());
                changeDocumentStatusSaveVO.setWorkFlowPojoList(workFlowPojoList);
            }
        } catch (Exception e) {
            LOG.error("contractServiceImpl.changeDocumentStatus 执行出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return changeDocumentStatusSaveVO;
    }

    @Override
    public void updateBatchLabelAttr(AddLabelDTO addLabelDTO, List<PaasFormDataEntityExt> addBatchList, Boolean addOrDelete) throws XbbException {
        ContractUpdateBatchDTO contractUpdateBatchDTO = new ContractUpdateBatchDTO();
        BeanUtil.copyProperties(addLabelDTO, contractUpdateBatchDTO);
        List<ContractUpdateDTO> contractList = new ArrayList<>();
        addBatchList.forEach(item -> {
            ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
            contractUpdateDTO.setData(item.getData());
            contractUpdateDTO.setId(item.getId());
            contractList.add(contractUpdateDTO);
        });
        contractUpdateBatchDTO.setContractUpdateList(contractList);
        updateBatch(contractUpdateBatchDTO);

    }

    @Override
    public ListAppVO formatWaitPrductOutstock(FormDataListDTO formDataListDTO) throws XbbException {
        // 待出库可见权限
        UserVO loginUser = formDataListDTO.getLoginUser();
        if (!loginUser.getPermSet().contains(ProPermissionAliasEnum.PENDING_OUTSTOCK_VIEW.getAlias()) && !loginUser.isAdminOrBoss()){
            throw new XbbException(PurchaseErrorCodeEnum.API_ERROR_225038);
        }
        List<AppListPojo> appListPojos = new ArrayList<>();
        String corpid = formDataListDTO.getCorpid();
        //获取当前合同模板下的合同数据
        formDataListDTO.setBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
        List<Long> contractIds= paasFormDataService.getContractsByEs(formDataListDTO);
        if (contractIds.isEmpty()) {
            return new ListAppVO();
        }
        //获取当前页合同产品关联数据
        Map<String, Object> param = contractProductService.getWaitOutStockParam(formDataListDTO, contractIds);
        List<ContractProductEntity> contractProductEntityList = contractProductService.findWaitOutProductEntitys(param);
        List<Long> contractIdList = new ArrayList<>();
        Set<Long> productIdSet = new HashSet<>();
        for (ContractProductEntity entity : contractProductEntityList) {
            productIdSet.add(entity.getProductId());
            productIdSet.add(entity.getParentId());
            contractIdList.add(entity.getContractId());
        }
        //获取待出库数量
        Map<String, Double> productEntityMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,Object> paramMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        paramMap.put("corpid", corpid);
        paramMap.put("refIdIn", contractIdList);
        paramMap.put("type", OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode());
        paramMap.put("del", 0);
        paramMap.put("columns","ref_id,product_id,ref_product_id,product_num");
        paramMap.put(ParameterConstant.ORDER_BY_STR, BasicConstant.SORT);
        List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(paramMap);
        for (OutstockProductEntity entity : outstockProductEntityList){
            String key;
            if (Objects.isNull(entity.getRefProductId())){
                key = entity.getRefId() + "_" + entity.getProductId();
            }else {
                key = entity.getRefId() + "_" + entity.getProductId() + "_" + entity.getRefProductId();
            }
            Double num = productEntityMap.getOrDefault(key,0D);
            num += entity.getProductNum();
            productEntityMap.put(key,num);
        }
        Iterator<ContractProductEntity> iterator = contractProductEntityList.iterator();
        while (iterator.hasNext()) {
            ContractProductEntity entity = iterator.next();
            String key = entity.getContractId() + "_" + entity.getProductId() + "_" + entity.getId();
            //先匹配新数据
            if (!productEntityMap.containsKey(key)) {
                //在匹配老数据
                key = entity.getContractId() + "_" + entity.getProductId();
                if (!productEntityMap.containsKey(key)) {
                    entity.setWaitOutStockNum(entity.getProductNum());
                    continue;
                }
            }
            Double outstockNum = productEntityMap.get(key);
            double productNum = entity.getProductNum() == null ? 0D : entity.getProductNum();
            double leftProductNum = Arith.sub(productNum, outstockNum);
            if (leftProductNum < 0.00000001 ) {
                productEntityMap.put(key,0D);
            } else {
                productEntityMap.put(key,leftProductNum);
            }
            entity.setWaitOutStockNum(productEntityMap.getOrDefault(key,0D));
        }
        //获取ES合同数据
        Map<Long, PaasFormDataEntity> contractMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        BoolQueryBuilder boolQueryContractBuilder = boolQuery();
        boolQueryContractBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryContractBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(), contractIdList));
        boolQueryContractBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_CONTRACT.getType()));
        XbbAggregatedPage<PaasFormDataEntity> esContractEntities = esHelper.findByPage(IndexTypeEnum.IDX_SAAS_CONTRACT, boolQueryContractBuilder, PaasFormDataEntity.class, Arrays.asList(FieldTypeEnum.DATAID.getAlias(), FieldTypeEnum.SERIALNO.getAlias(),"data"), 1, contractIdList.size(), null);
        for (PaasFormDataEntity entity : esContractEntities.getContent()) {
            contractMap.put(entity.getDataId(), entity);
        }
        //权限设置
        StringBuffer coUserListStr = new StringBuffer();
        StringBuffer mainUserListStr = new StringBuffer();
        this.processContractAndproductExplain(null,contractMap,contractProductEntityList,loginUser,corpid,formDataListDTO.getFormId(),formDataListDTO.getUserId(),coUserListStr,mainUserListStr);
        //获取ES产品数据
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(), productIdSet));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_PRODUCT.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_PRODUCT.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 0, productIdSet.size());
        searchRequest.source(sourceBuilder);
        //获取ES关联产品的具体信息
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt entity : esEntities.getContent()) {
            productMap.put(entity.getDataId(), entity);
        }
        //组装数据返回前端
        for (ContractProductEntity entity : contractProductEntityList) {
            //获取合同data
            PaasFormDataEntity contractEntity=contractMap.get(entity.getContractId());
            //产品数据
            PaasFormDataEntityExt productFormData=productMap.get(entity.getProductId());
            JSONObject contractData=contractEntity.getData();
            //待出库数量
            Double waitOutNum = entity.getWaitOutStockNum();
            Long contractId = entity.getContractId();
            JSONArray explains = new JSONArray();
            JSONObject others = new JSONObject();
            others.put("formId", contractEntity.getFormId());
            others.put("menuId", contractEntity.getMenuId());
            others.put(StringConstant.BUSINESS_TYPE, XbbRefTypeEnum.CONTRACT.getCode());
            others.put(StringConstant.DATA_ID, contractId);
            others.put("sourceBusinessType", XbbRefTypeEnum.PRODUCT.getCode());
            others.put("productId", entity.getProductId());
            others.put("linkId", entity.getId());
            others.put("productName", entity.getProductName());
            others.put("productImage", new ArrayList<>());
            if (Objects.nonNull(entity.getProductThumbnail()) && CollectionsUtil.isNotEmpty(JSONArray.parseArray(entity.getProductThumbnail()))) {
                others.put("productImage", JSONArray.parseArray(entity.getProductThumbnail()));
            } else if (Objects.nonNull(productMap.get(entity.getParentId()))) {
                PaasFormDataEntityExt parentEntity = productMap.get(entity.getParentId());
                JSONArray productImgs = FastJsonHelper.getJsonArrOrDefaultFromFormData(parentEntity.getData(), ProductEnum.PRODUCT_IMGS.getAttr(), new JSONArray());
                others.put("productImage", productImgs);
            }
            if (Objects.equals(formDataListDTO.getDistributorMark(), DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                String linkDistributor= contractData.getString(ContractEnum.LINK_CUSTOMER_HIDE.getAttr());
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW,ProductEnum.SPECIFICATION.getAttrName(),entity.getSpecification()));
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, OrderEnum.ORDER_NO.getAttrName(), contractEntity.getSerialNo()));
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, OrderEnum.LINK_DISTRIBUTOR.getAttrName(), linkDistributor));
                if (Objects.nonNull(entity.getRate())) {
                    explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitOutstockEnum.WAIT_OUTSTOCK_NUM.getAttrName(), Arith.div(waitOutNum,entity.getRate())));
                } else {
                    explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitOutstockEnum.WAIT_OUTSTOCK_NUM.getAttrName(), waitOutNum));
                }
            } else {
                String linkCustomer = contractData.getString(ContractEnum.LINK_CUSTOMER_HIDE.getAttr());
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW,ProductEnum.SPECIFICATION.getAttrName(),specificationModel.joinSpecification(entity.getSpecification())));
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.CONTRACT_NO.getAttrName(), contractEntity.getSerialNo()));
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.LINK_CUSTOMER.getAttrName(), linkCustomer));
                if (Objects.nonNull(entity.getRate())) {
                    explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitOutstockEnum.WAIT_OUTSTOCK_NUM.getAttrName(), Arith.div(waitOutNum,entity.getRate())));
                } else {
                    explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitOutstockEnum.WAIT_OUTSTOCK_NUM.getAttrName(), waitOutNum));
                }
            }
            appListPojos.add(new AppListPojo(entity.getProductId(), entity.getProductName(), explains, null, null, others));
        }
        ListAppVO listAppVO = new ListAppVO();
        listAppVO.setList(appListPojos);
        listAppVO.setTotalCount(contractIds.size());
        return listAppVO;
    }

    @Override
    public void formatWaitPurchaseExportValue(List<PaasFormDataEntityExt> paasFormDataESList, FormDataListDTO formDataListDTO) throws XbbException {
        DetailTabDataGetDTO detailTabDataGetDTO = new DetailTabDataGetDTO();
        detailTabDataGetDTO.setCorpid(formDataListDTO.getCorpid());
        detailTabDataGetDTO.setLoginUser(formDataListDTO.getLoginUser());
        detailTabDataGetDTO.setUserId(formDataListDTO.getUserId());
        //处理待采购产品详情
        for (PaasFormDataEntityExt paasFormDataEntityExt : paasFormDataESList){
            JSONObject data = paasFormDataEntityExt.getData();
            JSONArray productJSONArray = new JSONArray();
            detailTabDataGetDTO.setDataId(paasFormDataEntityExt.getDataId());
            WaitPurchaseProductVO waitPurchaseProductVO = saasListService.getWaitPurchaseProductDetail(detailTabDataGetDTO);
            List<WaitPurchaseProductPojo> waitPurchaseProductList = waitPurchaseProductVO.getWaitPurchaseProductList();
            for(WaitPurchaseProductPojo waitPurchaseProductPojo : waitPurchaseProductList){
                JSONArray supplierArray = waitPurchaseProductPojo.getSupplier();
                String supplier = "";
                if (CollectionUtils.isNotEmpty(supplierArray)){
                    supplier = supplierArray.getJSONObject(0).getString("name");
                }
                JSONObject waitInstockProduct = new JSONObject();
                waitInstockProduct.put(ProductEnum.NAME.getSaasAttr(),waitPurchaseProductPojo.getName());
                waitInstockProduct.put(ProductEnum.SERIALNO.getSaasAttr(),waitPurchaseProductPojo.getSerialNo());
                waitInstockProduct.put(ProductEnum.SPECIFICATION.getSaasAttr(),waitPurchaseProductPojo.getSpecification());
                waitInstockProduct.put("productUnit",waitPurchaseProductPojo.getProductUnit());
                waitInstockProduct.put(ProductEnum.SUPPLIER.getSaasAttr(),supplier);
                waitInstockProduct.put(ContractProductEnum.MEMO.getSaasAttr(),waitPurchaseProductPojo.getMemo());
                waitInstockProduct.put("marketNum",waitPurchaseProductPojo.getMarketNum());
                waitInstockProduct.put("purchaseNum",waitPurchaseProductPojo.getPurchaseNum());
                waitInstockProduct.put("instockNum",waitPurchaseProductPojo.getInstockNum());
                waitInstockProduct.put("outstockNum",waitPurchaseProductPojo.getOutstockNum());
                waitInstockProduct.put("waitOutstockNum",waitPurchaseProductPojo.getWaitOutstockNum());
                waitInstockProduct.put("advisePurchaseNum",waitPurchaseProductPojo.getAdvisePurchaseNum());
                productJSONArray.add(waitInstockProduct);
            }
            data.put("array_2",productJSONArray);
            //处理客户名称
            String clientName = "";
            if (CollectionUtils.isNotEmpty( data.getJSONArray(ContractBaseEnum.LINK_CUSTOMER.getAttr()))){
                clientName = data.getJSONArray(ContractBaseEnum.LINK_CUSTOMER.getAttr()).getJSONObject(0).getString("name");
            }
            data.put(ContractBaseEnum.LINK_CUSTOMER.getAttr(),clientName);
            String addTime = DateTimeUtil.getStringEpochSecond(data.getLong(PurchaseEnum.ADD_TIME.getAttr()), DateTimeUtil.SDF);
            String updateTime = DateTimeUtil.getStringEpochSecond(data.getLong(PurchaseEnum.UPDATE_TIME.getAttr()), DateTimeUtil.SDF);
            data.put(ContractEnum.ADD_TIME.getAttr(),addTime);
            data.put(ContractEnum.UPDATE_TIME.getAttr(),updateTime);
            data.remove("option_5");
        }
    }

    @Override
    public ContractUpdateBatchVO updateBatch(ContractUpdateBatchDTO contractUpdateBatchDTO) throws XbbException {
        ContractUpdateBatchVO contractUpdateBatchVO = new ContractUpdateBatchVO();
        try {
            String corpid = contractUpdateBatchDTO.getCorpid();
            List<UpdateDataEntity> updateList = new ArrayList<>();
            List<ContractUpdateDTO> contractUpdateList = contractUpdateBatchDTO.getContractUpdateList();
            if (Objects.nonNull(contractUpdateList) && !contractUpdateList.isEmpty()) {
                contractUpdateList.forEach((item) -> {
                    updateList.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), corpid));
                });
            }
            if (!updateList.isEmpty()) {
                contractModel.updateBatch(updateList, corpid);
            }
        } catch (Exception e) {
            LOG.error("contractServiceImpl.updateBatch 执行出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return contractUpdateBatchVO;
    }

    @Override
    public ContractUpdateBatchVO updateBatchImmediately(ContractUpdateBatchDTO contractUpdateBatchDTO) throws XbbException {
        ContractUpdateBatchVO contractUpdateBatchVO = new ContractUpdateBatchVO();
        try {
            String corpid = contractUpdateBatchDTO.getCorpid();
            List<UpdateDataEntity> updateList = new ArrayList<>();
            List<ContractUpdateDTO> contractUpdateList = contractUpdateBatchDTO.getContractUpdateList();
            if (Objects.nonNull(contractUpdateList) && !contractUpdateList.isEmpty()) {
                contractUpdateList.forEach((item) -> {
                    updateList.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), corpid));
                });
            }
            if (!updateList.isEmpty()) {
                contractModel.updateBatchImmediately(updateList, corpid);
            }
        } catch (Exception e) {
            LOG.error("contractServiceImpl.updateBatch 执行出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return contractUpdateBatchVO;
    }


    @Override
    public ContractUpdateBatchVO updateBatch(ContractUpdateBatchDTO contractUpdateBatchDTO, Boolean isUpdateTime) throws XbbException {
        ContractUpdateBatchVO contractUpdateBatchVO = new ContractUpdateBatchVO();
        try {
            String corpid = contractUpdateBatchDTO.getCorpid();
            List<UpdateDataEntity> updateList = new ArrayList<>();
            List<ContractUpdateDTO> contractUpdateList = contractUpdateBatchDTO.getContractUpdateList();
            if (Objects.nonNull(contractUpdateList) && !contractUpdateList.isEmpty()) {
                contractUpdateList.forEach((item) -> {
                    if (isUpdateTime) {
                        updateList.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), corpid));
                    } else {
                        updateList.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), corpid, item.getUpdateTime()));
                    }
                });
            }
            if (!updateList.isEmpty()) {
                contractModel.updateBatch(updateList, corpid);
            }
        } catch (Exception e) {
            LOG.error("contractServiceImpl.updateBatch (包含跟新时间)执行出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return contractUpdateBatchVO;
    }

    @Override
    public ContractUpdateBatchVO updateBatch(ContractUpdateBatchDTO contractUpdateBatchDTO, Boolean isUpdateTime, boolean needThrow) throws XbbException {
        ContractUpdateBatchVO contractUpdateBatchVO = new ContractUpdateBatchVO();
        try {
            String corpid = contractUpdateBatchDTO.getCorpid();
            List<UpdateDataEntity> updateList = new ArrayList<>();
            List<ContractUpdateDTO> contractUpdateList = contractUpdateBatchDTO.getContractUpdateList();
            if (Objects.nonNull(contractUpdateList) && !contractUpdateList.isEmpty()) {
                contractUpdateList.forEach((item) -> {
                    if (isUpdateTime) {
                        updateList.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), corpid));
                    } else {
                        updateList.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), corpid, item.getUpdateTime()));
                    }
                });
            }
            if (!updateList.isEmpty()) {
                contractModel.updateBatch(updateList, corpid, needThrow);
            }
        } catch (Exception e) {
            LOG.error("contractServiceImpl.updateBatch (包含跟新时间)执行出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return contractUpdateBatchVO;
    }

    /**
     * 一个合同中的产品可以重复添加，所以需要将合同中id相同的产品数量叠加
     *
     * @param contractProductEntityList 合同关联产品
     * @author GuoJun.HU
     * @since 3.19
     * 创建时间 2018/3/15 16:02
     */
    public Collection<ContractProductEntity> mergeContractProduct(List<ContractProductEntity> contractProductEntityList) {
        Map<Long, ContractProductEntity> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ContractProductEntity contractProduct : contractProductEntityList) {
            Long productId = contractProduct.getProductId();
            if (contractProductMap.containsKey(productId)) {
                ContractProductEntity mergeContractProduct = contractProductMap.get(productId);
                Double mergeProductNum = mergeContractProduct.getProductNum();
                Double productNum = contractProduct.getProductNum();
                Double mergeNum = Arith.add(mergeProductNum, productNum);

                // since汪汪11月，合并折扣，by lcx
                Double mergeDiscount = mergeContractProduct.getDiscount();
                Double discount = contractProduct.getDiscount();
                mergeDiscount = mergeDiscount == null ? 100D : mergeDiscount;
                discount = discount == null ? 100D : discount;
                try {
                    double newDiscount = Arith.div(Arith.add(Arith.mul(mergeDiscount, mergeProductNum), Arith.mul(discount, productNum)), mergeNum);
                    mergeContractProduct.setDiscount(newDiscount);
                } catch (Exception e) {
                    // 除零错误，一般不会有
                    mergeContractProduct.setDiscount(100D);
                }
                // 合并售价
                Double mergePrice = mergeContractProduct.getPrice();
                Double price = contractProduct.getPrice();
                mergePrice = mergePrice == null ? 0D : mergePrice;
                price = price == null ? 0D : price;
                try {
                    double newPrice = Arith.div(Arith.add(Arith.mul(mergePrice, mergeProductNum), Arith.mul(price, productNum)), mergeNum);
                    mergeContractProduct.setPrice(newPrice);
                } catch (Exception e) {
                    // 除零错误，一般不会有
                    mergeContractProduct.setPrice(0D);
                }

                mergeContractProduct.setProductNum(mergeNum);
                String memo = mergeContractProduct.getMemo();
                if (StringUtils.isNotEmpty(contractProduct.getMemo())) {
                    memo += "/" + contractProduct.getMemo();
                    mergeContractProduct.setMemo(memo);
                }
                contractProductMap.put(productId, mergeContractProduct);
            } else {
                contractProductMap.put(productId, contractProduct);
            }
        }
        return contractProductMap.values();
    }

    /**
     * 一个合同中的产品可以重复添加，所以需要将合同中id相同的产品数量叠加
     *
     * @param contractProductEntityList 合同关联产品
     * @throws XbbException
     * @author long.rao
     * @date 2019-08-01 17:10
     */
    public JSONArray mergeContractProduct(JSONArray contractProductEntityList) throws XbbException {
        if (contractProductEntityList.isEmpty()) {
            return contractProductEntityList;
        }
        Map<Long, JSONObject> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (Object contractProduct : contractProductEntityList) {
            JSONObject product = (JSONObject) contractProduct;
            Long productId = FastJsonHelper.getLongFromFormData(product, SelectProductEnum.PRODUCT.getAttr());
            if (contractProductMap.containsKey(productId)) {
                JSONObject mergeContractProduct = contractProductMap.get(productId);
                Double mergeProductNum = getDoubleOrDefaultFromFormData(mergeContractProduct, SelectProductEnum.NUM.getAttr(), 0D);
                Double productNum = getDoubleOrDefaultFromFormData(product, SelectProductEnum.NUM.getAttr(), 0D);
                Double mergeNum = Arith.add(mergeProductNum, productNum);

                // 合并数量
                mergeContractProduct.put(SelectProductEnum.NUM.getAttr(), mergeNum);
                contractProductMap.put(productId, mergeContractProduct);
            } else {
                contractProductMap.put(productId, product);
            }
        }
        JSONArray products = new JSONArray();
        products.addAll(contractProductMap.values());
        return products;
    }

    /**
     * 编辑的时候 获取合同关联的需要回显的其他费用信息
     *
     * @param corpid     公司id
     * @param contractId 合同id
     * @description:
     * @author long.rao
     * @date 2019-03-04 22:04
     */
    private JSONArray getContractSurchargeArray(String corpid, Long contractId, Long formId, JSONObject dataList) {
        List<ContractSurchargeEntity> contractSurchargeList = new ArrayList<>();
        if (contractId == null || Objects.equals(contractId, 0L)) {
            JSONObject others = dataList.getJSONObject(ContractEnum.PRODUCT.getAttr()).getJSONObject(BusinessConstant.OTHERS);
            JSONArray surchargeArray = new JSONArray();
            if (others != null) {
                surchargeArray = others.getJSONArray(StringConstant.SURCHARGE_LIST);
            }
            if (Objects.nonNull(surchargeArray)) {
                for (int i = 0; i < surchargeArray.size(); i++) {
                    JSONObject surchargeObject = surchargeArray.getJSONObject(i);
                    // 费用金额
                    Double amount = surchargeObject.getDouble(SurchargeShowEnum.AMOUNT.getAttr());
                    // 费用类型
                    Integer surchargeType = surchargeObject.getInteger(SurchargeShowEnum.SUTCHARGE_TYPE.getAttr());
                    // 费用名称
                    String surchargeName = surchargeObject.getString(SurchargeShowEnum.SURCHARGE_NAME.getAttr());
                    ContractSurchargeEntity contractSurchargeEntity = new ContractSurchargeEntity();
                    contractSurchargeEntity.setAmount(amount);
                    contractSurchargeEntity.setSurchargeType(surchargeType);
                    contractSurchargeEntity.setSurchargeName(surchargeName);
                    contractSurchargeList.add(contractSurchargeEntity);
                }
            }
        } else {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("contractId", contractId);
            param.put("del", 0);
            contractSurchargeList = contractSurchargeModel.findEntitys(param);
        }


        JSONArray surchargeList = surchargeModel.getAllSurcharge(corpid, formId);

        Map<String, Boolean> surchargeMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        JSONArray contractSurchargeArray = new JSONArray();
        for (ContractSurchargeEntity surcharge : contractSurchargeList) {
            surchargeMap.put(surcharge.getSurchargeName(), true);
            JSONObject surchargeObj = new JSONObject();
            surchargeObj.put("id", surcharge.getId());
            surchargeObj.put("surchargeName", surcharge.getSurchargeName());
            surchargeObj.put("surchargeType", surcharge.getSurchargeType());
            surchargeObj.put("amount", surcharge.getAmount());
            contractSurchargeArray.add(surchargeObj);
        }
        for (Object surcharge : surchargeList) {
            JSONObject surchargeObj = (JSONObject) surcharge;
            String surchargeName = surchargeObj.getString("surchargeName");
            if (surchargeMap.containsKey(surchargeName) && surchargeMap.get(surchargeName)) {
                continue;
            }
            contractSurchargeArray.add(surchargeObj);
        }

        return contractSurchargeArray;
    }


    public ContractOutstockDeleteVO calculateContractCostBatch(String corpid, List<Long> contractIdList, List<ContractEntityExt> contractList, List<Long> delOutstockIdList) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termQuery("data." + OutstockEnum.TYPE.getAttr(), OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
        boolQueryBuilder.filter(termsQuery("data." + OutstockEnum.REF_ID.getAttr(), contractIdList));
        boolQueryBuilder.mustNot(termsQuery(FieldTypeEnum.DATAID.getAlias(), delOutstockIdList));
        boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        List<PaasFormDataEntityExt> outstockList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_OUTSTOCK, boolQueryBuilder, PaasFormDataEntityExt.class, Arrays.asList(OutstockEnum.getAttrConnectData(OutstockEnum.REF_ID), FieldTypeEnum.DATAID.getAlias()));

        // 根据出库单获取对应的产品
        List<Long> dataIdIn = new ArrayList<>();
        outstockList.forEach(outstock -> dataIdIn.add(outstock.getDataId()));
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("del", 0);
        param.put("outstockIdIn", dataIdIn);
        List<OutstockProductEntity> outstockAllProductList = outstockProductModel.findEntitys(param);

        // 合同和关联的出库单的对应关系
        Map<Long, List<Long>> contractOutstockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ContractEntityExt contractEntity : contractList) {
            Long contractId = contractEntity.getId();
            List<Long> outstockIdList = contractOutstockMap.get(contractId);
            if (outstockIdList == null) {
                outstockIdList = new ArrayList<>();
            }
            for (PaasFormDataEntityExt outstock : outstockList) {
                Long refId = outstock.getData().getLong(OutstockEnum.REF_ID.getAttr());
                if (Objects.equals(contractId, refId)) {
                    outstockIdList.add(outstock.getDataId());
                }
            }
            contractOutstockMap.put(contractId, outstockIdList);
        }

        // 合同和出库产品的对应关系
        Map<Long, List<OutstockProductEntity>> contractOutstockProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ContractEntityExt contractEntity : contractList) {
            Long contractId = contractEntity.getId();
            List<Long> outstockIdList = contractOutstockMap.get(contractId);
            List<OutstockProductEntity> outstockProductEntityList = contractOutstockProductMap.get(contractId);
            if (outstockProductEntityList == null) {
                outstockProductEntityList = new ArrayList<>();
            }
            for (OutstockProductEntity outstockProductEntity : outstockAllProductList) {
                Long outstockId = outstockProductEntity.getOutWarehouseId();
                if (outstockIdList.contains(outstockId)) {
                    outstockProductEntityList.add(outstockProductEntity);
                }
            }
            contractOutstockProductMap.put(contractId, outstockProductEntityList);
        }

        // 合同和关联产品的对应关系
        List<ContractProductEntity> contractProductEntityList = contractProductModel.getContractProductByContractIdIn(corpid, contractIdList);
        Map<Long, List<ContractProductEntity>> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ContractEntityExt contractEntity : contractList) {
            Long contractId = contractEntity.getId();
            List<ContractProductEntity> contractProductList = contractProductMap.get(contractId);
            if (contractProductList == null) {
                contractProductList = new ArrayList<>();
            }
            for (ContractProductEntity contractProductEntity : contractProductEntityList) {
                if (Objects.equals(contractProductEntity.getContractId(), contractId)) {
                    contractProductList.add(contractProductEntity);
                }
            }
            contractProductMap.put(contractId, contractProductList);
        }

        List<ContractUpdateDTO> contractUpdateList = new ArrayList<>();
        for (ContractEntityExt contractEntity : contractList) {
            JSONObject contractData = contractEntity.getData();
            Long contractId = contractEntity.getId();
            List<OutstockProductEntity> outstockProductList = contractOutstockProductMap.get(contractId);
            List<ContractProductEntity> contractProductList = contractProductMap.get(contractId);

            // 计算合同出库成本：SUM（已出库产品数量*出库产品成本）
            double realCost = 0D;
            boolean isOld = false;
            // 合并的出库产品数量MAP
            Map<Long, Double> outstockProductNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 行对应数量
            Map<Long, Double> lineProductNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (OutstockProductEntity outstockProductEntity : outstockProductList) {
                if (Objects.isNull(outstockProductEntity.getRefProductId())) {
                    isOld = true;
                }
                Long productId = outstockProductEntity.getProductId();
                Double productNum = outstockProductEntity.getProductNum();
                productNum = productNum == null ? 0D : productNum;
                Double cost = outstockProductEntity.getCost();
                cost = cost == null ? 0D : cost;
                realCost = Arith.add(realCost, Arith.mul(productNum, cost));
                if (outstockProductNumMap.containsKey(productId)) {
                    outstockProductNumMap.put(productId, Arith.add(outstockProductNumMap.get(productId), productNum));
                } else {
                    outstockProductNumMap.put(productId, productNum);
                }
                if (!isOld) {
                    Long refProductId = outstockProductEntity.getRefProductId();
                    if (lineProductNumMap.containsKey(refProductId)) {
                        lineProductNumMap.put(refProductId, Arith.add(lineProductNumMap.get(refProductId), productNum));
                    } else {
                        lineProductNumMap.put(refProductId, productNum);
                    }
                }
            }

            // 算出合同中每种产品的平均价
            Map<Long, String> avgCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (ContractProductEntity contractProductEntity : contractProductList) {
                Long productId = contractProductEntity.getProductId();
                Double productNum = contractProductEntity.getProductNum() == null ? 0D : Math.abs(contractProductEntity.getProductNum());
                if (isOld) {
                    if (avgCostMap.containsKey(productId)) {
                        // map中已经有相同产品，需要计算平均值
                        String productInfo = avgCostMap.get(productId);
                        String[] split = productInfo.split(",");
                        Double preCost = StringUtil.toDouble((split[0]), 0D);
                        Double preNum = StringUtil.toDouble((split[1]), 0D);
                        Double nowCost = SaasProductHelp.calculateCost(preNum, preCost, productNum, contractProductEntity.getCost(), 1);
                        Double nowNum = Arith.add(preNum, productNum);
                        avgCostMap.put(productId, nowCost + "," + nowNum);
                    } else {
                        // 将产品id为key，合同产品平均价和数量拼接字符串为value存入map中
                        String productInfo = contractProductEntity.getCost() + "," + productNum;
                        avgCostMap.put(productId, productInfo);
                    }
                } else if (lineProductNumMap.containsKey(contractProductEntity.getId())) {
                    // 分行已出库数量计算
                    contractProductEntity.setContractOutstockNum(lineProductNumMap.get(contractProductEntity.getId()));
                }
            }
            double estimateCost = 0D;
            if (isOld) {
                // 老数据合同预估成本计算
                for (ContractProductEntity contractProductEntity : contractProductList) {
                    String productInfo = avgCostMap.get(contractProductEntity.getProductId());
                    if (StringUtils.isEmpty(productInfo)) {
                        continue;
                    }
                    String[] productInfos = productInfo.split(",");
                    if (outstockProductNumMap.containsKey(contractProductEntity.getProductId())) {
                        if (Arith.sub(contractProductEntity.getProductNum(), outstockProductNumMap.get(contractProductEntity.getProductId())) > 0) {
                            estimateCost = Arith.add(estimateCost, Arith.mul(Arith.sub(contractProductEntity.getProductNum(), outstockProductNumMap.get(contractProductEntity.getProductId())), StringUtil.toDouble((productInfos[0]), 0D)));
                        }
                    } else {
                        estimateCost = Arith.add(estimateCost, Arith.mul(contractProductEntity.getProductNum(), StringUtil.toDouble((productInfos[0]), 0D)));
                    }
                }
            } else {
                // 新数据合同预估成本计算
                for (ContractProductEntity contractProductEntity : contractProductList) {
                    if (Arith.sub(contractProductEntity.getProductNum(), contractProductEntity.getContractOutstockNum()) > 0) {
                        estimateCost = Arith.add(estimateCost, Arith.mul(contractProductEntity.getCost(), Arith.sub(contractProductEntity.getProductNum(), contractProductEntity.getContractOutstockNum())));
                    }
                }
            }

            // 计算退货退款的因素
            Boolean isJxcUse = paasAppService.validateAppEnable(null, XbbRefTypeEnum.JXC.getAlias(), SaasMarkEnum.SAAS.getCode(), corpid);
            Map<Long, Double> contractIdAndCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, Double> allContractCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            calculateContractRefundAmount(corpid, contractId, isJxcUse, contractIdAndCostMap, allContractCostMap);

            Double surchargeAmount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.OTHER_EXPENSE.getAttr(), BasicConstant.ZERO_DOUBLE);
            // 合同成本 = 合同产品成本 - 退货产品成本（入库单的产品成本：新数据用入库单的成本，旧数据用出库单的平均成本）+ 其他费用
            Double contractCost;
            if (contractIdAndCostMap.containsKey(contractId)) {
                Double cost = contractIdAndCostMap.get(contractId) == null ? BasicConstant.ZERO_DOUBLE : contractIdAndCostMap.get(contractId);
                contractCost = Arith.sub(Arith.add(Arith.add(estimateCost, realCost), surchargeAmount), cost);
            } else {
                contractCost = Arith.add(Arith.add(estimateCost, realCost), surchargeAmount);
            }

            JSONObject needUpdateContractData = new JSONObject();
            needUpdateContractData.put(ContractEnum.ESTIMATE_COST.getAttr(), Arith.round(estimateCost, 2));
            needUpdateContractData.put(ContractEnum.REAL_COST.getAttr(), Arith.round(realCost, 2));
            needUpdateContractData.put(ContractEnum.CONTRACT_COST.getAttr(), Arith.round(contractCost, 2));

            Double amount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.AMOUNT.getAttr(), 0D);
            //合同毛利 = 合同金额 - 退货金额（关联计算）- 合同成本（同上）
            Double grossProfit;
            if (allContractCostMap.containsKey(contractId)) {
                Double cost = allContractCostMap.get(contractId) == null ? BasicConstant.ZERO_DOUBLE : allContractCostMap.get(contractId);
                grossProfit = Arith.sub(Arith.sub(amount, contractCost), cost);
            } else {
                grossProfit = Arith.sub(amount, contractCost);
            }
            needUpdateContractData.put(ContractEnum.GROSS_PROFIT.getAttr(), Arith.round(grossProfit, 2));
            //合同毛利率
            if (Double.compare(amount, BasicConstant.ZERO_DOUBLE) != 0) {
                needUpdateContractData.put(ContractEnum.GROSS_PROFIT_RATE.getAttr(), Arith.round(Arith.div(grossProfit, amount), 4));
            } else {
                needUpdateContractData.put(ContractEnum.GROSS_PROFIT_RATE.getAttr(), BasicConstant.ZERO_DOUBLE);
            }
            Double finishAmount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
            Double badAmount = getDoubleOrDefaultFromFormData(contractData, ContractEnum.BAD_AMOUNT.getAttr(), 0D);
            Double unAmount = amount - finishAmount - badAmount;
            Double unInvoiceMoney = getDoubleOrDefaultFromFormData(contractData, ContractEnum.INVOICE_AMOUNT.getAttr(), 0D);
            Double cashProfit = Arith.sub(finishAmount, contractCost);
            needUpdateContractData.put(ContractEnum.CASH_PROFIT.getAttr(), cashProfit);
            needUpdateContractData.put(ContractEnum.UN_FINISH_AMOUNT.getAttr(), unAmount);
            double payPercent;
            //合同金额为0时候 收款比例为100%
            if (amount == 0) {
                payPercent = 1;
            } else {
                payPercent = Arith.round(Arith.div(finishAmount, amount), 4);
            }
            needUpdateContractData.put(ContractEnum.PAY_PERCENT.getAttr(), payPercent);
            needUpdateContractData.put(ContractEnum.UN_INVOICE_AMOUNT.getAttr(), amount - unInvoiceMoney);
            if (Double.compare(finishAmount, BasicConstant.ZERO_DOUBLE) != 0) {
                needUpdateContractData.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), Arith.round(Arith.div(cashProfit, finishAmount), 2));
            } else {
                needUpdateContractData.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), BasicConstant.ZERO_DOUBLE);
            }

            ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
            contractUpdateDTO.setId(contractId);
            contractUpdateDTO.setData(needUpdateContractData);
            contractUpdateList.add(contractUpdateDTO);
        }

        ContractOutstockDeleteVO contractOutstockDeleteVO = new ContractOutstockDeleteVO();
        contractOutstockDeleteVO.setOutstockList(outstockList);
        contractOutstockDeleteVO.setOutstockAllProductList(outstockAllProductList);
        contractOutstockDeleteVO.setContractOutstockMap(contractOutstockMap);
        contractOutstockDeleteVO.setContractOutstockProductMap(contractOutstockProductMap);
        contractOutstockDeleteVO.setContractProductMap(contractProductMap);
        contractOutstockDeleteVO.setContractUpdateList(contractUpdateList);
        return contractOutstockDeleteVO;
    }



    /**
     * 计算出合同总计需要退货金额以及退货合同总成本
     * contractIdAndCostMap 合同与红冲合同退货产品成本对应map
     * allContractCostMap 查询出所有关联原合同的退货金额
     */
    public void calculateContractRefundAmount(String corpid, Long contractId, boolean isJxcUse,
                                              Map<Long, Double> contractIdAndCostMap, Map<Long, Double> allContractCostMap) throws XbbException {
        contractAnalyticalService.calculateContractRefundAmount(corpid, contractId, isJxcUse, contractIdAndCostMap, allContractCostMap);
    }

    @Override
    public ContractDeleteBatchVO deleteBatch(ContractDeleteBatchDTO contractDeleteBatchDTO) throws XbbException {
        ContractDeleteBatchVO contractDeleteBatchVO = new ContractDeleteBatchVO();
        Integer businessType = contractDeleteBatchDTO.getBusinessType();
        XbbRefTypeEnum typeEnum = XbbRefTypeEnum.getByCode(businessType);
        // TODO 删除权限校验
        // TODO 如果该合同有回款单在审批中（待审批或审批中）则不让删除合同(批量时只要有一个有问题就返回错误)
        String corpid = contractDeleteBatchDTO.getCorpid();

        List<Long> deleteIdIn = contractDeleteBatchDTO.getDataIdList();
        List<Long> deleteIdIn4Log = new ArrayList<>(deleteIdIn);
        if (deleteIdIn.size() > BasicConstant.MAX_DEL_BATCH_COUNT) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100079.getCode(), String.format(SystemErrorCodeEnum.API_ERROR_100079.getMsg(), BasicConstant.MAX_DEL_BATCH_COUNT));
        }
        List<String> errorDataList = new ArrayList<>();
        Set<String> errorDataSet = new HashSet<>();
        Set<String> errorTypeSet = new HashSet<>();
        List<String> errorTypeList = new ArrayList<>();

        Map<String, String> noAndName = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> paymentIdIn = new ArrayList<>();
        try {
            Map<String, Object> param = BeanUtil.convertBean2Map(contractDeleteBatchDTO, true);
            param.put("idIn", deleteIdIn);
            param.put(ParameterConstant.COLUMNS, "id, serial_no, data, form_id");
            List<ContractEntityExt> contractList = contractModel.findEntitys(param);
            if (Objects.isNull(contractList)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }
            // 兼容es有，数据库没有的情况
            List<Long> esIds = new ArrayList<>();
            if (!contractList.isEmpty()) {
                List<Long> dbId = new ArrayList<>();
                for (ContractEntityExt entityExt : contractList) {
                    dbId.add(entityExt.getId());
                }
                for (Long id : deleteIdIn) {
                    if (!dbId.contains(id)) {
                        esIds.add(id);
                    }
                }
            } else {
                esIds = deleteIdIn;
            }
            if (!esIds.isEmpty()) {
                BoolQueryBuilder boolQueryBuilder = boolQuery();
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                boolQueryBuilder.filter(termsQuery("dataId", esIds));
                List<String> fieldList = Arrays.asList("id", FieldTypeEnum.SERIALNO.getAlias(), ContractEnum.getAttrConnectData(ContractEnum.NAME));
                List<ContractEntityExt> esContracts = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT, boolQueryBuilder, ContractEntityExt.class, fieldList);
                if (CollectionsUtil.isNotEmpty(esContracts)) {
                    contractList.addAll(esContracts);
                }
            }

            //paymentSheetInApprove之前表示在审批阶段的回款单，现在表示只要存在审批中的回款单，或存在回款单就不能删除
            List<Long> paymentSheetInApprove = new ArrayList<>();
            if (commonHelp.isOpenWorkFlow(corpid)) {
                BoolQueryBuilder boolQueryBuilder = boolQuery();
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
                boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.CONTRACT), deleteIdIn));
                boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.APPROVAL_PENDING.getType(), FlowStatusEnum.IN_APPROVAL.getType())));
                boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET.getType()));
                List<PaasFormDataEntityExt> paymentSheetFormDataList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET,
                        boolQueryBuilder, PaasFormDataEntityExt.class, Arrays.asList(PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.CONTRACT), FieldTypeEnum.DATAID.getAlias()));
                paymentSheetFormDataList.forEach(item -> {
                    JSONObject data = item.getData();
                    if (Objects.nonNull(data)) {
                        JSONArray contractIds = data.getJSONArray(PaymentSheetEnum.CONTRACT.getAttr());
                        for (Object obj : contractIds) {
                            if (Objects.nonNull(obj)) {
                                Long contractId = StringUtil.StringToLong(obj.toString());
                                if (Objects.nonNull(contractId)) {
                                    paymentSheetInApprove.add(contractId);
                                }
                            }
                        }
                    }
                });
            } else {
                List<PaasProcessDataEntity> paymentSheetProcessList = paasProcessDataModel.getInApprovalDataByIdConditionJsonArray(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), PaymentSheetEnum.CONTRACT.getAttr(), deleteIdIn, corpid, Arrays.asList(FlowStatusEnum.APPROVAL_PENDING.getType(), FlowStatusEnum.IN_APPROVAL.getType()));
                if (Objects.isNull(paymentSheetProcessList)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
                }
                paymentSheetProcessList.forEach(item -> {
                    JSONObject data = JSON.parseObject(item.getData());
                    if (Objects.nonNull(data)) {
                        JSONArray contractIds = data.getJSONArray(PaymentSheetEnum.CONTRACT.getAttr());
                        for (Object obj : contractIds) {
                            if (Objects.nonNull(obj)) {
                                Long contractId = StringUtil.StringToLong(obj.toString());
                                if (Objects.nonNull(contractId)) {
                                    paymentSheetInApprove.add(contractId);
                                }
                            }
                        }
                    }
                });
            }
            List<Object> linkIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            deleteIdIn.forEach(id -> {
                linkIdIn.add(id);
            });
            Map<Long, PaasFormDataEntityExt> paymentSheetEntityExtMap = paymentSheetService.getEsDataMapByLinkIds(corpid, linkIdIn, PaymentSheetEnum.CONTRACT);
            for (ContractEntityExt item : contractList) {
                Long contractId = item.getId();
                PaasFormDataEntityExt paymentSheetEntityExt = paymentSheetEntityExtMap.get(contractId);
                if (Objects.nonNull(paymentSheetEntityExt)) {
                    paymentSheetInApprove.add(contractId);
                }
            }
            // 查询合同还有没有关联的退货退款单 youli.chen 未关联退货退款的正常删除

            Set<Long> linkRefundId = new HashSet<>();
            List<String> field = new ArrayList<>();
            List<PaasFormDataEntityExt> entityExts;
            // 查询合同还有没有关联的退货退款单 未关联退货退款的正常删除
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder queryBuilder = boolQuery();
            queryBuilder.filter(termQuery("corpid.keyword", corpid));
            queryBuilder.filter(termsQuery("data." + RefundEnum.CONTRACT_ID.getAttr(), deleteIdIn));
            queryBuilder.filter(termQuery("del", 0));
            // 设置join_field
            queryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_REFUND.getType()));
            sourceBuilder.query(queryBuilder);
            field.add(RefundEnum.CONTRACT_ID.getAttr());
            entityExts = paasEsModel.list(sourceBuilder, IndexTypeEnum.IDX_SAAS_REFUND, field, null, null);
            if (Objects.nonNull(entityExts)) {
                for (PaasFormDataEntityExt entity : entityExts) {
                    JSONObject data = entity.getData();
                    Long contractId = data.getLong(RefundEnum.CONTRACT_ID.getAttr());
                    linkRefundId.add(contractId);
                }
            }

            //paymentSheetInApprove之前表示在审批阶段的回款单，现在表示只要存在审批中的回款单，或存在回款单就不能删除
            List<Long> invoiceInApprove = new ArrayList<>();
            if (commonHelp.isOpenWorkFlow(corpid)) {
                BoolQueryBuilder boolQueryBuilder = boolQuery();
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
                boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.CONTRACT), deleteIdIn));
                boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.APPROVAL_PENDING.getType(), FlowStatusEnum.IN_APPROVAL.getType())));
                boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_INVOICE.getType()));
                List<PaasFormDataEntityExt> invoiceFormDataList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_INVOICE,
                        boolQueryBuilder, PaasFormDataEntityExt.class, Arrays.asList(PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.CONTRACT), FieldTypeEnum.DATAID.getAlias()));
                invoiceFormDataList.forEach(item -> {
                    JSONObject data = item.getData();
                    if (Objects.nonNull(data)) {
                        JSONArray contractIds = data.getJSONArray(PaymentSheetEnum.CONTRACT.getAttr());
                        for (Object obj : contractIds) {
                            Long contractId = StringUtil.StringToLong((String) obj);
                            if (Objects.nonNull(contractId)) {
                                invoiceInApprove.add(contractId);
                            }
                        }
                    }
                });
            } else {
                // 获取销项发票是否在审批中
                List<PaasProcessDataEntity> invoiceProcessList = paasProcessDataModel.getInvoiceInApprovalDataByIdConditionJsonArray(XbbRefTypeEnum.INVOICE.getCode(), PaymentSheetEnum.CONTRACT.getAttr(), deleteIdIn, corpid, Arrays.asList(FlowStatusEnum.APPROVAL_PENDING.getType(), FlowStatusEnum.IN_APPROVAL.getType()));
                invoiceProcessList.forEach(item -> {
                    JSONObject data = JSON.parseObject(item.getData());
                    if (Objects.nonNull(data)) {
                        JSONArray contractIds = data.getJSONArray(PaymentSheetEnum.CONTRACT.getAttr());
                        for (Object obj : contractIds) {
                            Long contractId = StringUtil.StringToLong((String) obj);
                            if (Objects.nonNull(contractId)) {
                                invoiceInApprove.add(contractId);
                            }
                        }
                    }
                });
            }

            Map<String, Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            map.put("contractIdIn", deleteIdIn);
            map.put("corpid", corpid);
            map.put("del", DelEnum.NORMAL);
            List<InvoiceRelationshipEntity> invoiceRelationshipEntityList = invoiceRelationshipModel.findEntitys(map);
            Set<Long> invoiceRelationshipContractList = new HashSet<>();
            Map<Long, Long> contractRefInvoiceId = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            invoiceRelationshipEntityList.forEach(invoiceRelationshipEntity -> {
                invoiceRelationshipContractList.add(invoiceRelationshipEntity.getContractId());
                contractRefInvoiceId.put(invoiceRelationshipEntity.getContractId(), invoiceRelationshipEntity.getInvoiceId());
            });


            List<Long> physicalDelIds = new ArrayList<>();
            physicalDelIds.addAll(deleteIdIn);
            // 查询合同是否有下游单据销售出库单，生成了关联销售出库单的不允许删除
            Set<Long> linkOutstackId = new HashSet<>();
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
            boolQueryBuilder.filter(termsQuery("data." + OutstockEnum.REF_ID.getAttr(), deleteIdIn));
            boolQueryBuilder.filter(termQuery("data." + OutstockEnum.TYPE.getAttr(), OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
            boolQueryBuilder.filter(termQuery("del", 0));
            // 设置join_field
            boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_CONTRACT_OUTSTOCK.getType()));
            sourceBuilder.query(boolQueryBuilder);
            field.clear();
            field.add(OutstockEnum.REF_ID.getAttr());
            entityExts = paasEsModel.list(sourceBuilder, IndexTypeEnum.IDX_SAAS_CONTRACT_OUTSTOCK, field, null, null);

            if (Objects.nonNull(entityExts)) {
                for (PaasFormDataEntityExt entity : entityExts) {
                    JSONObject data = entity.getData();
                    Long contractId = data.getLong(OutstockEnum.REF_ID.getAttr());
                    linkOutstackId.add(contractId);
                }
            }
            for (ContractEntityExt item : contractList) {
                String no = item.getSerialNo();
                String name = FastJsonHelper.getStringOrDefaultFromFormData(item.getData(), ContractEnum.NAME.getAttr(), "");
                noAndName.put(no, name);
            }
            // 未关联退货退款的合同ID
            deleteIdIn.removeAll(linkRefundId);
            deleteIdIn.removeAll(linkOutstackId);
            for (ContractEntityExt item : contractList) {
                JSONObject contractObj = item.getData();
                if (linkRefundId.contains(item.getId())) {
                    errorTypeSet.add(String.format(I18nMessageUtil.getMessage(ErrorMessageConstant.HAVE_REFUND_NOT_DEL), typeEnum.getName()));
                    errorDataSet.add(item.getSerialNo());
                }
                // 合同关联销售出库单，不允许删除
                if (linkOutstackId.contains(item.getId())) {
                    errorTypeSet.add(String.format(I18nMessageUtil.getMessage(ErrorMessageConstant.HAVE_OUTSTACK_NOT_DEL), typeEnum.getName()));
                    errorDataSet.add(item.getSerialNo());
                }
                // 回款单存在审批中的，不允许删除
                if (paymentSheetInApprove.contains(item.getId())) {
                    deleteIdIn.remove(item.getId());
                    errorTypeSet.add(I18nMessageUtil.getMessage(ErrorMessageConstant.HAVE_PAYMENT_SHEET_NOT_DEL));
                    errorDataSet.add(item.getSerialNo());
                }

                if (invoiceInApprove.contains(item.getId())) {
                    deleteIdIn.remove(item.getId());
                    errorTypeSet.add(I18nMessageUtil.getMessage(ErrorMessageConstant.HAVE_INVOICE_APPROVE_NOT_DEL));
                    errorDataSet.add(item.getSerialNo());
                }
                if (invoiceRelationshipContractList.contains(item.getId())) {
                    //Long invoiceId = contractRefInvoiceId.getOrDefault(item.getId(),0L);
                    deleteIdIn.remove(item.getId());
                    errorTypeSet.add(String.format(I18nMessageUtil.getMessage(ErrorMessageConstant.HAVE_CONTRACT_INVOICE_NOT_DEL), typeEnum.getName()));
                    errorDataSet.add(item.getSerialNo());
                }
            }
            errorDataList.addAll(errorDataSet);
            errorTypeList.addAll(errorTypeSet);
            if (!deleteIdIn.isEmpty()) {
                contractDeleteBatchVO.setDeleteIds(deleteIdIn);
                if (BasicConstant.ONE.equals(contractDeleteBatchDTO.getSingleFlag())
                        && BasicConstant.ONE.equals(deleteIdIn.size())) {
                    contractModel.deleteByKey(deleteIdIn.get(0), corpid);
                } else {
                    contractModel.deleteBatch(deleteIdIn, corpid);
                }
                // 删除合同的同时删除市场活动
                marketActivityForStaticHelp.deleteBatchMarketActivity(deleteIdIn, XbbRefTypeEnum.CONTRACT ,corpid, BasicConstant.ONE);
                // 删除合同联系人
                ContractUserDeleteBatchDTO contractUserDeleteBatchDTO = new ContractUserDeleteBatchDTO();
                BeanUtil.copyProperties(contractDeleteBatchDTO, contractUserDeleteBatchDTO);
                contractUserDeleteBatchDTO.setContractIdIn(deleteIdIn);
                contractUserService.deleteBatch(contractUserDeleteBatchDTO);
            }

            // 删除相关应收款
            if (!deleteIdIn.isEmpty()) {
                BoolQueryBuilder booleanQuery = boolQuery();
                booleanQuery.filter(termsQuery(StringConstant.JSON_DATA + StringConstant.POINT + PaymentEnum.CONTRACT.getAttr(), deleteIdIn));
                booleanQuery.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
                booleanQuery.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
                List<String> fieldList = new ArrayList<>();
                fieldList.add(FieldTypeEnum.DATAID.getAlias());
                List<PaasFormDataEntityExt> paymentList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PAYMENT, booleanQuery, PaasFormDataEntityExt.class, fieldList);
                if (Objects.isNull(paymentList)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
                }
                paymentList.forEach((item) -> {
                    paymentIdIn.add(item.getDataId());
                });
                if (!paymentIdIn.isEmpty()) {
                    PaymentDeleteBatchDTO paymentDeleteBatchDTO = new PaymentDeleteBatchDTO();
                    BeanUtil.copyProperties(contractDeleteBatchDTO, paymentDeleteBatchDTO, true);
                    paymentDeleteBatchDTO.setDataIdList(paymentIdIn);
                    paymentService.deleteBatch(paymentDeleteBatchDTO);
                }
                // 删除合同业绩分配
                ContractPerformanceDeleteBatchDTO contractPerformanceDeleteBatchDTO = new ContractPerformanceDeleteBatchDTO();
                BeanUtil.copyProperties(contractDeleteBatchDTO, contractPerformanceDeleteBatchDTO, true);
                contractPerformanceDeleteBatchDTO.setContractIdIn(deleteIdIn);
                contractPerformanceService.deleteBatch(contractPerformanceDeleteBatchDTO);
                // 删除合同产品
                ContractProductDeleteBatchDTO contractProductDeleteBatchDTO = new ContractProductDeleteBatchDTO();
                BeanUtil.copyProperties(contractDeleteBatchDTO, contractProductDeleteBatchDTO, true);
                contractProductDeleteBatchDTO.setContractIdIn(deleteIdIn);
                contractProductService.deleteBatch(contractProductDeleteBatchDTO);
                // 删除采购结存表数据  回退智能补货数据
                List<Long> contractIdList = contractProductDeleteBatchDTO.getContractIdIn();
                if (CollectionsUtil.isNotEmpty(contractIdList)){
                    // 查询出所有合同产品数据
                    Map<String, Object> conparam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    conparam.put("corpid", corpid);
                    conparam.put("contractIdIn", contractIdList);
                    conparam.put(ParameterConstant.ORDER_BY_STR, BasicConstant.SORT);
                    List<ContractProductEntity> contractProductEntityList = contractProductModel.findEntitys(conparam);
                    if (CollectionsUtil.isNotEmpty(contractProductEntityList)){
                        // key = productid = value = 删除的产品总量
                        Map<Long,Double> smartMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        // key = onkey value = 删除的总量
                        Map<String,Double> purchaseBalanceMap = new HashMap<>();
                        List<Long> productIdList = new ArrayList<>();
                        List<String> onlyKeyList = new ArrayList<>();
                        for (ContractProductEntity entity : contractProductEntityList) {
//                            Long contractId = entity.getContractId();
                            Long productId = entity.getProductId();
                            String onlyKey = entity.getId() + "_" + productId;
                            Double productNum = entity.getProductNum();
                            productIdList.add(productId);
                            // 封装智能补货需要的数据
                            if (smartMap.containsKey(productId)){
                                smartMap.put(productId,Arith.add(smartMap.get(productId),productNum));
                            }else {
                                smartMap.put(productId,productNum);
                            }
                            // 封装采购结存需要的数据
                            if (purchaseBalanceMap.containsKey(onlyKey)){
                                purchaseBalanceMap.put(onlyKey,Arith.add(purchaseBalanceMap.get(onlyKey),productNum));
                            }else {
                                purchaseBalanceMap.put(onlyKey,productNum);
                                onlyKeyList.add(onlyKey);
                            }

                        }
                        // 更新智能补货数据
                        List<SmartReplenishmentEntity> smartReplenishmentEntityList = smartReplenishmentModel.getByProductList(corpid, productIdList);
                        if(CollectionsUtil.isNotEmpty(smartReplenishmentEntityList)){
                            for (SmartReplenishmentEntity entity : smartReplenishmentEntityList) {
                                Long productId = entity.getProductId();
                                if(smartMap.containsKey(productId)){
                                    entity.setWaitOutstockNum(Arith.sub(entity.getWaitOutstockNum(), smartMap.get(productId)));
                                }
                            }
                            smartReplenishmentModel.updateBatch(smartReplenishmentEntityList,corpid);
                        }
                        // 更新结存表del
                        purchaseBalanceModel.batchUpdateDel(corpid,onlyKeyList,DelEnum.DELETE.getDel());
                    }
                }
                ContractSurchargeDeteleBatchDTO contractSurchargeDeteleBatchDTO = new ContractSurchargeDeteleBatchDTO();
                BeanUtil.copyProperties(contractDeleteBatchDTO, contractSurchargeDeteleBatchDTO, true);
                contractSurchargeDeteleBatchDTO.setContractIdIn(deleteIdIn);
                contractSurchargeService.deleteBatchByContractId(contractSurchargeDeteleBatchDTO);
                PushNotifyDeleteDTO pushNotifyDeleteDTO = new PushNotifyDeleteDTO();
                BeanUtil.copyProperties(contractDeleteBatchDTO, pushNotifyDeleteDTO);
                pushNotifyDeleteDTO.setNeedDelNotifyIdList(deleteIdIn);
                pushNotifyDeleteDTO.setBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
                pushNotifyService.deletePushNotify(pushNotifyDeleteDTO);
            }

            // TODO 删除相关审批记录

            //生成删除日志(区分单个删除和批量删除)
            String userId = contractDeleteBatchDTO.getUserId();
            String userName = contractDeleteBatchDTO.getLoginUserName();
            List<String> list = new ArrayList<>();
            Iterator<String> iterator = noAndName.keySet().iterator();
            while (iterator.hasNext()) {
                String key = iterator.next();
                String value = noAndName.get(key);
                if (StringUtil.isEmpty(value)) {
                    list.add(key);
                } else {
                    StringBuilder sb = new StringBuilder();
                    sb.append(key).append("（").append(value).append("）");
                    list.add(sb.toString());
                }
            }
            OperateTypeEnum operateTypeEnum = OperateTypeEnum.DELETE;
            if (Objects.equals(1, contractDeleteBatchDTO.getIsBusinessRule())) {
                operateTypeEnum = OperateTypeEnum.RULE_DELETE;
            }
            String nameStr = StringUtils.join(list, "、");


            Integer singleFlag = contractDeleteBatchDTO.getSingleFlag();
            if(Objects.equals(0,singleFlag)){
                List<InfoArrPojo> infoArrPojoList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                InfoArrPojo infoArrPojo = new InfoArrPojo();
                infoArrPojo.setTitle(OperationLogHelp.setDetailTitle());
                infoArrPojo.setContent(nameStr);
                infoArrPojoList.add(infoArrPojo);
                DeleteBatchOperationLogDTO deleteBatchOperationLogDTO = new DeleteBatchOperationLogDTO( corpid,  userId,  userName, null,  operateTypeEnum,
                        null,  nameStr,  null,  deleteIdIn4Log.size(),contractDeleteBatchDTO.getHttpHeader(),contractDeleteBatchDTO.getFormId(),contractDeleteBatchDTO.getBusinessType());
                OperationLogHandle operationLogHandle = operationLogFactory.getOperationLogHandle(SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CONTRACT.getCode());
                operationLogHandle.deleteBatch(infoArrPojoList,deleteBatchOperationLogDTO);
            } else if (Objects.equals(1,singleFlag)&&CollectionsUtil.isNotEmpty(deleteIdIn4Log)){
                DeleteOperationLogDTO deleteOperationLogDTO = new DeleteOperationLogDTO(corpid,userId,userName,null,operateTypeEnum,deleteIdIn4Log.get(0).toString(),nameStr,null,contractDeleteBatchDTO.getHttpHeader(),contractDeleteBatchDTO.getFormId(),contractDeleteBatchDTO.getBusinessType());
                OperationLogHandle operationLogHandle = operationLogFactory.getOperationLogHandle(SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CONTRACT.getCode());
                operationLogHandle.delete(deleteOperationLogDTO);
            }

            // 动态
            try {
                if(!Objects.equals(DistributorMarkEnum.DISTRIBUTOR.getCode(),contractDeleteBatchDTO.getDistributorMark())) {
                    DynamicStrategy dynamicStrategy = dynamicStrategyFactory.getDynamicStrategy(ProjectLogEnum.CONTRACT_DELETE.getSubType());
                    FormDataDeleteBatchDTO formDataDeleteBatchDTO = new FormDataDeleteBatchDTO();
                    BeanUtil.copyProperties(contractDeleteBatchDTO, formDataDeleteBatchDTO);
                    dynamicStrategy.batchDelete(DynamicDeleteDTO.initDynamicDeleteDTO(formDataDeleteBatchDTO, deleteIdIn, null));
                }
            } catch (Exception e) {
                LOG.error("删除合同动态失败：", e);
            }
        } catch (Exception e) {
            updateRollback(contractDeleteBatchDTO.getDataIdList(), corpid);
            paymentService.updateRollback(paymentIdIn, corpid);
            contractPerformanceService.updateRollbackByContractId(contractDeleteBatchDTO.getDataIdList(), corpid);
            contractUserService.updateRollbackByContractId(contractDeleteBatchDTO.getDataIdList(), corpid);
            contractProductService.updateRollbackByContractId(contractDeleteBatchDTO.getDataIdList(), corpid);
            LOG.error("contractServiceImpl.deleteBatch 执行出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        contractDeleteBatchVO.setErrorDataList(errorDataList);
        contractDeleteBatchVO.setErrorTypeList(errorTypeList);
        contractDeleteBatchVO.setErrorDataMemo(SaasDeleteHelp.getErrorDataMemo(errorTypeList, errorDataList));
        return contractDeleteBatchVO;
    }

    /**
     * 根据应收款ID回滚回款单
     *
     * @param paymentIdIn
     * @param corpid
     * @throws XbbException
     */
    @Override
    public void updateRollbackPaymentSheetByPayment(List<Long> paymentIdIn, String corpid) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.PAYMENT), paymentIdIn));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
        List<String> fieldList = new ArrayList<>();
        fieldList.add(StringConstant.DATA_ID);
        List<PaasFormDataEntityExt> paymentSheetList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        if (Objects.isNull(paymentSheetList)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        List<Long> dataIdIn = new ArrayList<>();
        paymentSheetList.forEach(item -> {
            if (Objects.nonNull(item.getDataId())) {
                dataIdIn.add(item.getDataId());
            }
        });
        paymentSheetService.updateRollback(dataIdIn, corpid);
    }

    @Override
    public ListAppVO formatContractAppList(FormDataListDTO formDataListDTO, List<PaasFormDataEntityExt> esEntities, Integer totalCount) throws XbbException {
        List<AppListPojo> appListPojos = new ArrayList<>();
        String corpid = formDataListDTO.getCorpid();
        Map<String, FieldAttrEntity> explainMap = formDataListDTO.getExplainMap();

        List<Long> contractIdIn = new ArrayList<>(totalCount);
        contractIdIn.add(-1L);
        for (PaasFormDataEntityExt entity : esEntities) {
            contractIdIn.add(entity.getDataId());
        }

        //有提醒的合同
        List<Long> pushNotifyList = new ArrayList<>();
        saasListHelp.getPushNotify(contractIdIn, corpid, XbbRefTypeEnum.CONTRACT.getCode(), pushNotifyList);

        FieldAttrEntity contactAmountExplain = explainMap.get(ContractEnum.AMOUNT.getAttr());
        boolean amountVisible = false;
        if (contactAmountExplain != null && Objects.equals(contactAmountExplain.getVisible(), 1)) {
            amountVisible = true;
        }
        for (PaasFormDataEntityExt contractEntity : esEntities) {
            JSONObject data = contractEntity.getData();
            Long contractId = contractEntity.getDataId();
            String searNo = contractEntity.getSerialNo() == null ? "" : contractEntity.getSerialNo();
            Long appId = contractEntity.getAppId();
            Long menuId = contractEntity.getMenuId();
            Long formId = contractEntity.getFormId();
            Integer isRed = getIntegerOrDefaultFromFormData(data, ContractEnum.IS_RED.getAttr(), 0);
            String amount = FastJsonHelper.getStringOrDefaultFromFormData(data, ContractEnum.AMOUNT.getAttr(), "0");

            String unit = I18nMessageUtil.getMessage(TabConstant.YUAN);
            amount += unit;
            if (!amountVisible) {
                amount = StringConstant.INVISIBLE_VALUE;
            }

            JSONObject superScript = new JSONObject();
            JSONObject others = new JSONObject();

            /*-- 角标 -*/
            superScript.put("message", pushNotifyList.contains(contractId) ? 1 : 0);
            // 归档标志：1代表归档，2代表未归档
            JSONObject tagData = contractEntity.getTagData();
            if (Objects.equals(tagData.getInteger("isAchived"), 1)) {
                superScript.put("archived", 1);
            }
            /*-- 标签 -*/

            /*-- 其他 -*/
            others.put("amount", amount);
            others.put("isRed", isRed);

            List<SummaryDataPoJo> summaryList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, BasicConstant.SUMMARY_LIST, new JSONArray()).toJavaList(SummaryDataPoJo.class);
            List<SummaryDataPoJo> lablesList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, BasicConstant.LABELS_LIST, new JSONArray()).toJavaList(SummaryDataPoJo.class);
            // 进行了退货退款的合同塞一个标签(退货合同)
            //是否包含红冲合同（即是否为退货合同）
            Integer hasRed = getIntegerOrDefaultFromFormData(data, ContractEnum.HAS_RED.getAttr(), 0);
            if (Objects.equals(hasRed, 1)) {
                SummaryDataPoJo dataPoJo = new SummaryDataPoJo();
                String name = I18nMessageUtil.getMessage(I18nStringConstant.RETURN_CONTRACT);
                dataPoJo.setAttr("");
                dataPoJo.setType("red");
                dataPoJo.setAttrName(name);
                dataPoJo.setFieldType(3);
                List<Object> objectList = new ArrayList<>();
                Object obj = name;
                objectList.add(obj);
                dataPoJo.setValue(objectList);
                lablesList.add(0, dataPoJo);
            }
            List<FormTitlePoJo> titleList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, BasicConstant.TITLE_LIST, new JSONArray()).toJavaList(FormTitlePoJo.class);
            //app系统关联数据展示需要 others 和 superScript
            data.put("others", others);
            data.put("superScript", superScript);

            appListPojos.add(new AppListPojo(contractId, titleList, summaryList, superScript, lablesList, others, appId, menuId, formId));
        }
        ListAppVO listAppVO = new ListAppVO();
        listAppVO.setList(appListPojos);
        listAppVO.setTotalCount(totalCount);
        return listAppVO;
    }

    @Override
    public ContractEditAttrUpdateVO batchEditAttr(ContractEditAttrUpdateDTO contractEditAttrUpdateDTO) throws XbbException {
        ContractEditAttrUpdateVO contractEditAttrUpdateVO = new ContractEditAttrUpdateVO();
        String userId = contractEditAttrUpdateDTO.getUserId();
        String corpid = contractEditAttrUpdateDTO.getCorpid();
        String loginUserName = contractEditAttrUpdateDTO.getLoginUserName();
        Integer fieldType = contractEditAttrUpdateDTO.getFieldType();
        Integer isBatch = contractEditAttrUpdateDTO.getIsBatch();
        Long formId = contractEditAttrUpdateDTO.getFormId();
        Integer businessType = contractEditAttrUpdateDTO.getBusinessType();
        boolean isFieldDependence = contractEditAttrUpdateDTO.getIsFieldDependence();
        try {
            // TODO 权限校验
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", contractEditAttrUpdateDTO.getCorpid());
            param.put("idIn", contractEditAttrUpdateDTO.getDataIdList());
            List<ContractEntityExt> contractList = contractModel.findEntitys(param);
            if (Objects.isNull(contractList)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }
            //团队权限校验前置了
//            List<Long> errorIdList = userTeamHelp.batchTeamMembersCheckEditPermission(contractEditAttrUpdateDTO.getBusinessType(), contractEditAttrUpdateDTO.getSaasMark(),
//                    contractEditAttrUpdateDTO.getMenuId(), contractEditAttrUpdateDTO.getLoginUser(), contractEditAttrUpdateDTO.getDistributorMark(),
//                    contractEditAttrUpdateDTO.getDataIdList(), new HashMap<>());
            List<Long> errorIdList = new ArrayList<>();
            List<String> errorNameList = new ArrayList<>();
            Integer count = 0;
            for (ContractEntityExt item : contractList) {
                JSONObject data = item.getData();
                if (Objects.nonNull(data)
                        && Objects.nonNull(data.get(ContractEnum.IS_RED.getAttr()))
                        && data.getInteger(ContractEnum.IS_RED.getAttr()) == 1) {
                    count++;
                }else if (errorIdList.contains(item.getId())) {
                    errorNameList.add(data.getString(ContactEnum.NAME.getAttr()));
                }
            }
            if (count > 0) {
                throw new XbbException(RefundErrorCodeEnum.API_ERROR_215011);
            }
            String nameStr = StringUtils.join(errorNameList, "、");
            if (CollectionUtils.isNotEmpty(errorNameList)) {
                throw new XbbException(UserTeamErrorCodeEnum.API_ERROR_271038.getCode(), String.format(UserTeamErrorCodeEnum.API_ERROR_271038.getMsg(), nameStr));
            }
            ContractUpdateBatchDTO contractUpdateBatchDTO = new ContractUpdateBatchDTO();
            BeanUtil.copyProperties(contractEditAttrUpdateDTO, contractUpdateBatchDTO);
            List<ContractUpdateDTO> contractUpdateList = new ArrayList<>();
            List<Long> updateIdList = new ArrayList<>();
            //是否批量编辑了合同签订日期字段
            boolean isEditSignTime = false;
            //注：不是依赖字段 则singleAttrValuePojo是一个blank，可以作为是否是依赖字段的判断
            AttrValuePojo singleAttrValuePojo = ProSaveHelp.getSingleAttrValuePojo(isFieldDependence, contractEditAttrUpdateDTO.getFieldEditedList());
            if (ContractEnum.STATUS.getAttr().equals(singleAttrValuePojo.getAttr())) {
                DictionaryListDTO dictionaryListDTO = new DictionaryListDTO();
                BeanUtil.copyProperties(contractEditAttrUpdateDTO, dictionaryListDTO);
                dictionaryListDTO.setType(DictionaryEnum.CONTRACT_STATUS.getType());
                DictionaryVO dictionaryVO = dataDictionaryModel.list(dictionaryListDTO);
                if (Objects.isNull(dictionaryVO)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
                }
                List<DictionaryPojo> dictionaryList = dictionaryVO.getList();
                Map<String, DictionaryPojo> dictionaryMap = new HashMap<>(dictionaryList.size());
                DictionaryPojo newStatusEntity = new DictionaryPojo();
                for (DictionaryPojo item : dictionaryList) {
                    if (item.getEnable() == 1) {
                        dictionaryMap.put(item.getValue(), item);
                        if (singleAttrValuePojo.getValue().equals(item.getValue())) {
                            newStatusEntity = item;
                        }
                    }
                }
                for (ContractEntityExt item : contractList) {
                    JSONObject data = item.getData();
                    if (Objects.nonNull(data)
                            && Objects.nonNull(data.get(ContractEnum.STATUS.getAttr()))) {
                        ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
                        contractUpdateDTO.setId(item.getId());
                        updateIdList.add(item.getId());
                        JSONObject updateData = new JSONObject();
                        if (newStatusEntity == null || !newStatusEntity.getEnable().equals(1)) {
                            updateData.put(ContractEnum.STATUS.getAttr(), 0);
                            updateData.put(ContractEnum.IF_STATISTIC.getAttr(), 0);
                        }/* else if (newStatusEntity.getAddtionnalField().equals(-1)
                                && dictionaryMap.containsKey(data.getInteger(ContractEnum.STATUS.getAttr()))
                                && !dictionaryMap.get(data.getInteger(ContractEnum.STATUS.getAttr())).getAddtionnalField().equals(-1)) {
                            //原来不是预签约状态，现在想改成预签约状态，但是预签约状态不是手动设置的
                            //"该合同状态不能主动设置"
                            if (!modelMap.containsKey("contractStatusException")) {
                                modelMap.put("contractStatusException", 0);
                            }
                            Integer countNum = (Integer) modelMap.get("contractStatusException");
                            countNum++;
                            modelMap.put("contractStatusException", countNum);
                            continue;
                        } */ else {
                            Integer ifStatistic = Objects.equals(newStatusEntity.getValue(), ContractStatusEnum.TERMINATION.getCode()) ? 0 : 1;
                            updateData.put(ContractEnum.STATUS.getAttr(), singleAttrValuePojo.getValue());
                            updateData.put(ContractEnum.IF_STATISTIC.getAttr(), ifStatistic);
                        }
                        contractUpdateDTO.setData(updateData);
                        contractUpdateList.add(contractUpdateDTO);
                    }
                }
            } else if(Objects.equals(ContractEnum.SIGN_TIME.getAttr(),contractEditAttrUpdateDTO.getAttr())){
                isEditSignTime = true;
                contractEditAttrUpdateDTO.getDataIdList().forEach((item) -> {
                    ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
                    contractUpdateDTO.setId(item);
                    updateIdList.add(item);
                    JSONObject data = new JSONObject();
                    data.put(contractEditAttrUpdateDTO.getAttr(), contractEditAttrUpdateDTO.getValue());
                    contractUpdateDTO.setData(data);
                    contractUpdateList.add(contractUpdateDTO);
                });
            } else {
                contractEditAttrUpdateDTO.getDataIdList().forEach((item) -> {
                    ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
                    contractUpdateDTO.setId(item);
                    updateIdList.add(item);
                    JSONObject data = ProSaveHelp.getBatchUpdateData(contractEditAttrUpdateDTO.getFieldEditedList());
                    contractUpdateDTO.setData(data);
                    contractUpdateList.add(contractUpdateDTO);
                });
            }
            if (!contractUpdateList.isEmpty()) {
                contractUpdateBatchDTO.setContractUpdateList(contractUpdateList);
                updateBatch(contractUpdateBatchDTO);
                if (ContractEnum.STATUS.getAttr().equals(singleAttrValuePojo.getAttr())) {
                    // TODO 推送

                }
            }
            contractEditAttrUpdateVO.setUpdateIdList(updateIdList);
            // 批量编辑日志
            List<String> updataItemList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<Long> dataIdList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (ContractEntityExt item : contractList) {
                dataIdList.add(item.getId());
            }
            if(Objects.nonNull(isBatch)) {
                List<ContractEntityExt> newContractList = contractModel.findEntitys(param);
                List<PaasFormDataEntity> newDataList = new ArrayList<>();
                List<PaasFormDataEntity> oldDataList = new ArrayList<>();
                BeanUtil.copyPropertiesList(contractList,oldDataList,PaasFormDataEntity.class);
                BeanUtil.copyPropertiesList(newContractList,newDataList,PaasFormDataEntity.class);
                OperationLogHandle operationLogHandle = operationLogFactory.getOperationLogHandle(contractEditAttrUpdateDTO.getSaasMark(),contractEditAttrUpdateDTO.getBusinessType());
                UpdateBatchOperationLogDTO updateBatchOperationLogDTO = new UpdateBatchOperationLogDTO(corpid,userId,loginUserName,dataIdList.size(),contractEditAttrUpdateDTO.getHttpHeader(),
                        contractEditAttrUpdateDTO.getLogFieldEditedList(), newDataList, oldDataList, formId, businessType,isBatch);;
                operationLogHandle.updateBatch(updateBatchOperationLogDTO);
            }
            //异步线程去更新客户的最新签订合同时间
            boolean finalIsEditSignTime = isEditSignTime;
            Runnable runnable = () -> {
                try {
                    //批量更新合同的签订时间，需要同步更新关联客户的合同最后签订时间
                    if (finalIsEditSignTime){
                        for (ContractEntityExt item : contractList) {
                            JSONObject data = item.getData();
                            if (Objects.nonNull(data) && Objects.nonNull(data.get(ContractEnum.SIGN_TIME.getAttr()))) {
                                //客户更新签订时间
                                Long newSignTime = Long.parseLong(contractEditAttrUpdateDTO.getValue().toString());
                                Long customerId = data.getLongValue(ContractEnum.LINK_CUSTOMER.getAttr());
                                publicSeniorRuleHelp.updateCustomerContractSignTime(newSignTime,corpid,customerId,item.getId());
                            }
                        }
                    }
                } catch (XbbException e) {
                    LOG.error("更新客户的最近合同签订时间", e);
                }
            };
            threadPoolBeanConfig.addUpdateDelThreadPool().execute(runnable);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("contractServiceImpl.batchEditAttr 执行出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return contractEditAttrUpdateVO;
    }

    private boolean contractSpecialPermissionsCheck(ContractEditAttrUpdateDTO contractEditAttrUpdateDTO, UserTeamEntity userTeamEntity) throws XbbException {
        String corpid = contractEditAttrUpdateDTO.getCorpid();
        Integer businessType = contractEditAttrUpdateDTO.getBusinessType();
        Integer saasMark = contractEditAttrUpdateDTO.getSaasMark();
        Long menuId = contractEditAttrUpdateDTO.getMenuId();
        Long dataId = contractEditAttrUpdateDTO.getDataIdList().get(0);
        UserVO loginUser = contractEditAttrUpdateDTO.getLoginUser();
        UserEntity userEntity = new UserEntity();
        BeanUtil.copyProperties(loginUser, userEntity);
        Integer dataPermission = userModel.getDataPermission(userEntity, SaasMarkEnum.getByCode(saasMark), businessType, menuId);
        Boolean isAdminOrBoss = loginUser.isAdminOrBoss();
        Boolean hasMaxPermission = Objects.equals(DataPermissionEnum.ALL.getCode(), dataPermission);
        if (isAdminOrBoss || hasMaxPermission) {
            return true;
        }
        if (Objects.isNull(userTeamEntity)) {
            //代表是有这条数据的数据权限，但是不在团队中；非本人、超管的其他数据权限，比如主管。
            List<String> userIdIn = userModel.getSubIdListByDataPermission(dataPermission, loginUser.getUserId(), corpid, BasicConstant.ONE, false);
            IndexTypeEnum indexTypeEnum = null;
            if (Objects.equals(SaasMarkEnum.PAAS.getCode(), saasMark)) {
                indexTypeEnum = IndexTypeEnum.IDX_FORM_DATA_TEAM;
            }else {
                indexTypeEnum = indexTypeModel.getByCode(corpid, businessType);
            }

            BoolQueryBuilder boolQueryBuilder = boolQuery();
            boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
            boolQueryBuilder.filter(termQuery(SubFormTransferEnum.getEsAttrByIndexTypeAndDbAttr(indexTypeEnum, FieldTypeEnum.DATAID.getAlias()), dataId));
            boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
            boolQueryBuilder.filter(termsQuery(SubFormTransferEnum.getEsAttrByIndexTypeAndDbAttr(indexTypeEnum, ParameterConstant.USER_ID), userIdIn));
            ElasticAggsPojo elasticAggsPojo = esHelper.elasticAggs(indexTypeEnum, boolQueryBuilder, null);
            return StringUtil.toInt(elasticAggsPojo.getTotal(), 0) > 0;
        }else {
            Integer isMain = userTeamEntity.getIsMain();
            if (Objects.equals(BasicConstant.ONE, isMain)) {
                //负责人都是可编辑的
                return true;
            }else if (Objects.equals(BasicConstant.ZERO, isMain)) {
                //如果是协同人就需要判断当前协同人是不是有编辑权限
                Integer formPermission = userTeamEntity.getFormPermission();
                if (Objects.isNull(formPermission)) {
                    formPermission = FormCoUserDefaultPermissionEnum.getDefaultPerByBus(businessType);
                }
                if (Objects.equals(formPermission, CoUserPermissionEnum.READ.getPermissionValue())) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public List<PaasFormDataEntityExt> getContractEsDataListByCustomerId(String corpid, Long customerId) throws XbbException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery("data." + ContractEnum.LINK_CUSTOMER.getAttr(), customerId));
        boolQueryBuilder.filter(termQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.IS_RED), 0));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_CONTRACT.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_CONTRACT.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 0, PaasConstant.ES_MAX_PAGE_SIZE);
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        return esEntities.getContent();
    }

    @Override
    public List<AggSumVO> avgBySumList(List<AggSumVO> numCountList, FormDataSummaryDTO formDataSummaryDTO, List<AggSumDTO> aggAvgFieldList, NativeSearchQuery searchQuery) throws XbbException {
        List<AggSumVO> aggAvgResultList = new ArrayList<>();
        if (numCountList.isEmpty()) {
            return aggAvgResultList;
        }
        Map<String, AggSumVO> numListMap = new HashMap<>(numCountList.size());
        numCountList.forEach((item) -> {
            numListMap.put(item.getAttr(), item);
        });
        for (AggSumDTO item : aggAvgFieldList) {
            String attr = item.getAttr();
            AggSumVO aggSumVO = new AggSumVO();
            aggSumVO.setAttr(attr);
            aggSumVO.setName(item.getName());
            Integer accuracy = item.getFieldAttrEntity().getAccuracy();
            if (ContractEnum.GROSS_PROFIT_RATE.getAttr().equals(attr)) {
                Double grossProfitTotal = 0D;
                if (Objects.nonNull(numListMap.get(ContractEnum.GROSS_PROFIT.getAttr())) && Objects.nonNull(numListMap.get(ContractEnum.GROSS_PROFIT.getAttr()).getValue())) {
                    grossProfitTotal = Double.parseDouble(numListMap.get(ContractEnum.GROSS_PROFIT.getAttr()).getValue().toString());
                }
                Double allAmount = 0D;
                if (Objects.nonNull(numListMap.get(ContractEnum.AMOUNT.getAttr())) && Objects.nonNull(numListMap.get(ContractEnum.AMOUNT.getAttr()).getValue())) {
                    allAmount = Double.parseDouble(numListMap.get(ContractEnum.AMOUNT.getAttr()).getValue().toString());
                }
                // 合同毛利率 = 合同毛利/合同金额
                Double grossProfitRate = allAmount == 0D ? 0D : Arith.div(grossProfitTotal, allAmount) * 100;
                String value = attrDecimalPrecisionHelper.formatMoneyPrecision(grossProfitRate, accuracy);
                aggSumVO.setValue(value + "%");
            } else if (ContractEnum.CASH_PROFIT_RATE.getAttr().equals(attr)) {
                // 已收金额=合同金额-未收金额
                Double finishAmount = 0D;
                if (Objects.nonNull(numListMap.get(ContractEnum.FINISH_AMOUNT.getAttr())) && Objects.nonNull(numListMap.get(ContractEnum.FINISH_AMOUNT.getAttr()).getValue())) {
                    finishAmount = Double.parseDouble(numListMap.get(ContractEnum.FINISH_AMOUNT.getAttr()).getValue().toString());
                }
                Double cashProfit = 0D;
                if (Objects.nonNull(numListMap.get(ContractEnum.CASH_PROFIT.getAttr())) && Objects.nonNull(numListMap.get(ContractEnum.CASH_PROFIT.getAttr()).getValue())) {
                    cashProfit = Double.parseDouble(numListMap.get(ContractEnum.CASH_PROFIT.getAttr()).getValue().toString());
                }
                // 现金毛利率 = 现金毛利/已收金额
                Double cashProfitRate = finishAmount == 0D ? 0D : Arith.div(cashProfit, finishAmount) * 100;
                String value = attrDecimalPrecisionHelper.formatMoneyPrecision(cashProfitRate, accuracy);
                aggSumVO.setValue(value + "%");
            } else if (Objects.equals(attr, ContractEnum.DISCOUNT.getAttr())) {
                List<AggSumDTO> summaryAttrList = new ArrayList<>();
                AggSumDTO aggSumDTO = new AggSumDTO();
                aggSumDTO.setAttr(ContractEnum.DISCOUNT.getAttr());
                aggSumDTO.setField(ContractEnum.getEsAttr4Keyword(ContractEnum.DISCOUNT));
                aggSumDTO.setName(ContractEnum.DISCOUNT.getAttrName());
                summaryAttrList.add(aggSumDTO);

                List<AggSumVO> discount = esHelper.aggAvgByField(IndexTypeEnum.IDX_SAAS_CONTRACT.getIndex(), IndexTypeEnum.IDX_SAAS_CONTRACT.getType(), summaryAttrList, searchQuery);
                if (!discount.isEmpty()) {
                    AggSumVO aggSumVO1 = discount.get(0);
                    aggSumVO.setValue(aggSumVO1.getValue());
                }
            }
            aggAvgResultList.add(aggSumVO);
        }
        return aggAvgResultList;
    }

    @Override
    public void updateContractCostWhenOutstock(ContractUpdateWhenOutstockDTO contractUpdateWhenOutstockDTO, ContractEntityExt contractEntityExt) throws XbbException {
        contractHelp.updateContractCostWhenOutstock(contractUpdateWhenOutstockDTO, contractEntityExt);
    }

   /* @Override
    public BusinessProductListVO getContractProductList(BusinessProductListDTO businessProductListDTO, JSONObject dataList) throws XbbException {
        BusinessProductListVO businessProductListVO = new BusinessProductListVO();
        try {
            String corpid = businessProductListDTO.getCorpid();
            UserEntity userEntity = userModel.getByKey(businessProductListDTO.getUserId() , corpid);
            Long outstockId = businessProductListDTO.getOutstockId();
            Long instockId = businessProductListDTO.getInstockId();
            Long contractId = businessProductListDTO.getDataId();
            ContractEntityExt contract = contractModel.getByKey(contractId, corpid);
            if (contract == null) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001.getCode(), MessageConstant.FORM_DATA_NOT_EXIST);
            }
            JSONObject contractData = contract.getData();
            Integer fromInstock = businessProductListDTO.getFromInstock();
            Integer fromOutstock = businessProductListDTO.getFromOutstock();
            boolean isInstock = Objects.equals(fromInstock, 1);
            boolean isOutstock = Objects.equals(fromOutstock, 1);
            List<ContractProductEntity> contractProductList = new ArrayList<>();
            Map<String, Object> modelMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            modelMap.put(ParameterConstant.CORPID, corpid);
            modelMap.put("contractId", contractId);
            modelMap.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            modelMap.put(ParameterConstant.PAGE,  businessProductListDTO.getPage());
            PageHelper pageHelper = PageHelperUtil.getPageHelper(modelMap, contractProductModel, businessProductListDTO.getPageSize());
            if (isOutstock) {
                CompanyConfigHasBeenEnableDTO hasBeenEnableDTO = new CompanyConfigHasBeenEnableDTO();
                BeanUtil.copyProperties(businessProductListDTO, hasBeenEnableDTO);
                hasBeenEnableDTO.setAlias(CompanyConfigEnum.CONTRACT_OUTSTOCK_OVER_SET.getAlias());
                CompanyConfigHasBeenEnableVO companyConfigHasBeenEnableVO = companyConfigService.hasBeenEnable(hasBeenEnableDTO);
                if (Objects.isNull(companyConfigHasBeenEnableVO)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
                }
                Integer enable = companyConfigHasBeenEnableVO.getEnable();
                if (Objects.equals(enable, 1)) {
                    contractProductList = getContractProductList(corpid, contractId);
                    // 将id相同的产品合并
                    Collection<ContractProductEntity> mergeContractProduct = mergeContractProduct(contractProductList);
                    contractProductList.clear();
                    contractProductList.addAll(mergeContractProduct);
                } else {
                    //获取合同剩余未出库产品信息及数量
                    List<Long> outstockIdList = new ArrayList<>();
                    if (Objects.nonNull(outstockId) && !Objects.equals(outstockId, 0L)) {
                        outstockIdList = new ArrayList<>();
                        outstockIdList.add(outstockId);
                    }
                    contractProductList = getSurplusProduct(corpid, contractId, null, outstockIdList);
                }
            } else if(isInstock){
                //获取红冲合同剩余未入库产品信息及数量
                List<Long> instockIdList;
                if (Objects.nonNull(instockId) && !Objects.equals(instockId, 0L)) {
                    instockIdList = new ArrayList<>();
                    instockIdList.add(instockId);
                }
                contractProductList = getRedContractOutstockProducts(corpid, contractId);
            } else {
                contractProductList = (List<ContractProductEntity>) PageHelperUtil.getEntityList(modelMap, pageHelper, contractProductModel);
            }
            Set<Long> productIds = new HashSet<>();
            productIds.add(-1L);
            Map<Long, ContractProductEntity> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (ContractProductEntity item : contractProductList) {
                productIds.add(item.getProductId());
                contractProductMap.put(item.getProductId(), item);
            }
            // 用于存放当前启用的产品模板解释的map
            Map<String, FieldAttrEntity> nowProductExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<String, FieldAttrEntity> nowStockProductExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            saasAddHelp.getRelativeProductExplainsFromDB(corpid, nowProductExplainMap, nowStockProductExplainMap);
            // 获取产品
            List<PaasFormDataEntityExt> productList = productService.getProductListById(productIds, corpid, null);
            Set<Long> parentIdInSet = new HashSet<>();
            Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(productList.size());
            productList.forEach((item)->{
                productMap.put(item.getDataId(), item);
                JSONObject data = item.getData();
                if (Objects.nonNull(data) && Objects.nonNull(data.getLong(ProductEnum.PARENT_ID.getAttr()))) {
                    parentIdInSet.add(data.getLong(ProductEnum.PARENT_ID.getAttr()));
                }
            });
            // 获取父类产品
            List<PaasFormDataEntityExt> parentProductList = productService.getProductListById(parentIdInSet, corpid, null);
            Map<Long, PaasFormDataEntityExt> parentProductMap = new HashMap<>(productList.size());
            parentProductList.forEach((item)->{
                parentProductMap.put(item.getDataId(), item);
            });

            Map<String, FieldAttrEntity> explainMap = paasFormExplainService.getFieldAttrMapByBusinessType(businessProductListDTO.getBusinessType(), corpid);
            // 返回给前端的接受对象
            JSONObject productFinalObj = new JSONObject();
            JSONArray productArray = new JSONArray();
            Map<Integer, JSONObject> productSortMap = new TreeMap<>();
            JSONObject othersObj = new JSONObject();
            // 与productList同级，返回对应关联产品的attr
            String attrStr = OutstockEnum.PRODUCT.getAttr();
            // 得到该公司设置的金额小数精度
            // TODO 小数精度使用各字段内的精度
//            Integer amountPrecision = companyConfigService.getAmountPrecision(corpid);

            FieldAttrEntity priceFieldAttrEntity = new FieldAttrEntity();
            if(nowProductExplainMap.containsKey(ProductEnum.PRICE.getSaasAttr())) {
                priceFieldAttrEntity = nowProductExplainMap.get(ProductEnum.PRICE.getSaasAttr());
            }
            Integer amountPrecision = priceFieldAttrEntity.getAccuracy();

            if (isOutstock) {
                Integer showSalePriceFlag = 0;
                FormConfigEntity formConfigEntity = formConfigModel.getEntityByBusinessType(corpid, XbbRefTypeEnum.OUTSTOCK.getCode(), FormConfigEnum.OUTSTOCK_DISPLAY_SALE_PRICE.getConfig());
                if (Objects.nonNull(formConfigEntity) && Objects.equals(formConfigEntity.getConfigValue(), "1")) {
                    showSalePriceFlag = 1;
                }
                for (ContractProductEntity item : contractProductList) {
                    Long productId = item.getProductId();
                    PaasFormDataEntityExt product = productMap.get(productId);
                    if (product == null) {
                        continue;
                    }
                    JSONObject data = product.getData();
                    PaasFormDataEntityExt parent = parentProductMap.get(data.getLong(ProductEnum.PARENT_ID.getAttr()));
                    if (parent == null) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
                    }
                    String productName = data.getString(ProductEnum.NAME.getAttr());
                    OutstockProductEntityExt outstockProduct = new OutstockProductEntityExt();
                    outstockProduct.setParentId(item.getParentId());
                    outstockProduct.setProductId(productId);
                    outstockProduct.setProductName(productName);

                    if (Objects.nonNull(parent)) {
                        outstockProduct.setProductNo(parent.getData().getString(ProductEnum.PRODUCT_NO.getAttr()));
                    } else {
                        outstockProduct.setProductNo(data.getString(ProductEnum.PRODUCT_NO.getAttr()));
                    }
                    String specification = data.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr());
                    outstockProduct.setProductSpecification(specificationModel.joinSpecification(specification));
                    outstockProduct.setProductNum(item.getProductNum());
                    outstockProduct.setMemo(item.getMemo());
                    outstockProduct.setProductUnit(data.getString(ProductEnum.UNIT.getAttr()));
                    outstockProduct.setBarcode(data.getString(ProductEnum.BARCODE.getAttr()));
                    ProductStockInfoEntity productStockInfoEntity = new ProductStockInfoEntity();
                    productStockInfoEntity.setBatch("");
                    outstockProduct.setProductStockInfoEntity(productStockInfoEntity);
                    // 库存设置
                    outstockProduct.setStock(data.getDouble(ProductEnum.STOCK.getAttr()));
                    // 获取关联产品的产品平均成本作为出库成本
                    outstockProduct.setCost(data.getDouble(ProductEnum.COST.getAttr()));
                    // 销售出库增加售价和折扣列，since汪汪11月
                    outstockProduct.setDiscount(item.getDiscount());
                    // 此处的价格是售价，而不是单价
                    outstockProduct.setProductPrice(item.getPrice());
                    // 构建格式化出库产品的DTO
                    FormatOutstockProductDTO formatOutstockProductDTO = new FormatOutstockProductDTO();
                    BeanUtil.copyProperties(businessProductListDTO, formatOutstockProductDTO);
                    formatOutstockProductDTO.setUserEntity(userEntity);

                    formatOutstockProductDTO.setOutstockProductExt(outstockProduct);
                    formatOutstockProductDTO.setNowProductExplainMap(nowProductExplainMap);
                    formatOutstockProductDTO.setNowStockProductExplainMap(nowStockProductExplainMap);

                    JSONObject productObj = outstockService.getFormatProductJsonArray(formatOutstockProductDTO);

                    if (Objects.equals(XbbRefTypeEnum.OUTSTOCK.getCode(), businessProductListDTO.getSourceBusinessType())) {
                        //为销售出库单，如果表单设置没有设置 销售价格展示在销售出库单中,去除后面几个字段值
                        addSalePriceAndDiscountExplains(productObj, outstockProduct, product, amountPrecision, showSalePriceFlag);
                    }

                    productSortMap.put(item.getSort(), productObj);
                }
            } else if (isInstock) {
                attrStr = InstockEnum.PRODUCT.getAttr();
                // 获取入库单之前关联的退货退款产品
                List<Long> instockIds = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<String, Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                map.put(ParameterConstant.CORPID, corpid);
                map.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
                Map<String, Object> dataParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                dataParam.put(InstockEnum.REF_ID.getAttr(), businessProductListDTO.getRefundDataId());
                dataParam.put(InstockEnum.TYPE.getAttr(), InstockTypeEnum.RED_CONTRACT_INSTOCK.getCode());
                List<PaasFormDataEntityExt> paasFormDataEntityExts = jsonSetService.selectByCondition(XbbRefTypeEnum.INSTOCK.getCode(), dataParam, map);
                if (!paasFormDataEntityExts.isEmpty()) {
                    paasFormDataEntityExts.forEach((item) -> instockIds.add(item.getId()));
                }
                Map<Long, Double> inProductIdAndSum = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                List<InstockProductEntity> instockProductList = instockProductModel.getProductsByInstockIdIn(corpid, instockIds);
                if (!instockProductList.isEmpty()) {
                    for (InstockProductEntity instockProductEntity : instockProductList) {
                        Double productNum = instockProductEntity.getProductNum();
                        if (inProductIdAndSum.containsKey(instockProductEntity.getProductId())) {
                            productNum = Arith.add(productNum, inProductIdAndSum.get(instockProductEntity.getProductId()));
                        }
                        inProductIdAndSum.put(instockProductEntity.getProductId(), productNum);
                    }
                }

                for (ContractProductEntity item : contractProductList) {
                    Long productId = item.getProductId();
                    PaasFormDataEntityExt product = productMap.get(productId);
                    if (product == null) {
                        continue;
                    }
                    JSONObject data = product.getData();
                    PaasFormDataEntityExt parent = parentProductMap.get(data.getLong(ProductEnum.PARENT_ID.getAttr()));
                    if (parent == null) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
                    }
                    JSONObject parentData = parent.getData();
                    String productName = data.getString(ProductEnum.NAME.getAttr());
                    InstockProductEntityExt instockProduct = new InstockProductEntityExt();
                    BeanUtil.copyProperties(item, instockProduct);
                    instockProduct.setProductName(productName);
                    instockProduct.setProductNo(parent != null ? parentData.getString(ProductEnum.PRODUCT_NO.getAttr()) : data.getString(ProductEnum.PRODUCT_NO.getAttr()));
                    String specification = data.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr());
                    instockProduct.setProductSpecification(specificationModel.joinSpecification(specification));
                    instockProduct.setProductUnit(data.getString(ProductEnum.UNIT.getAttr()));
                    instockProduct.setCost(saasUpdateHelp.getDoubleOrDefaultFromFormData(data, ProductEnum.COST.getAttr(), 0.00D));
                    if(inProductIdAndSum.containsKey(productId)){
                        Double newNum = Arith.sub(item.getProductNum(), inProductIdAndSum.get(productId));
                        if(newNum == 0D){
                            continue;
                        }
                        Double proNum = newNum > 0 ? newNum : item.getProductNum();
                        instockProduct.setProductNum(proNum);
                    }
                    instockProduct.setBarcode(data.getString(ProductEnum.BARCODE.getAttr()));
                    // 构建格式化出库产品的DTO
                    FormatInstockProductDTO formatInstockProductDTO = new FormatInstockProductDTO();
                    BeanUtil.copyProperties(businessProductListDTO, formatInstockProductDTO);
                    formatInstockProductDTO.setUserEntity(userEntity);
                    formatInstockProductDTO.setInstockProductExt(instockProduct);
                    formatInstockProductDTO.setNowProductExplainMap(nowProductExplainMap);
                    formatInstockProductDTO.setNowStockProductExplainMap(nowStockProductExplainMap);

                    JSONObject productObj = instockService.getFormatProductJsonArray(formatInstockProductDTO);
                    productSortMap.put(item.getSort(), productObj);
                }
            } else {
                double totalProductMoney = 0D;
                boolean totalProductMoneyHide = false;
                Map<Long, Double> alreadyOutstockProductMap = getAlreadyOutstockProductMap(contract);
                Map<Long, Double> contractProductNumMap = getContractProductNumMap(contractProductList);
//                Integer numberPrecision = companyConfigService.getNumberPrecision(corpid);

                FieldAttrEntity numberFieldAttrEntity = new FieldAttrEntity();
                if(nowProductExplainMap.containsKey(ProductEnum.STOCK.getSaasAttr())) {
                    numberFieldAttrEntity = nowProductExplainMap.get(ProductEnum.STOCK.getSaasAttr());
                }
                Integer numberPrecision = numberFieldAttrEntity.getAccuracy();
                for(ContractProductEntity item : contractProductList){
                    Long productId = item.getProductId();
                    PaasFormDataEntityExt product = productMap.get(productId);
                    if (product == null) {
                        continue;
                    }
                    JSONObject data = product.getData();
                    PaasFormDataEntityExt parent = parentProductMap.get(data.getLong(ProductEnum.PARENT_ID.getAttr()));
                    if (parent == null) {
                        continue;
                    }
                    JSONObject parentData = parent.getData();
                    String productNo = parentData == null ? "" : parentData.getString(ProductEnum.PRODUCT_NO.getAttr());
                    Double productPrice = 0.0;
                    if (item.getPrice().equals(-1d)) {
                        productPrice = 0d;
                    } else {
                        productPrice = item.getPrice();
                    }
                    Double productNum = attrDecimalPrecisionHelper.setPrecision(item.getProductNum(), numberPrecision);
                    productPrice = attrDecimalPrecisionHelper.setPrecision(productPrice, amountPrecision);
                    FormatRelativeProductEntityExt formatProduct = new FormatRelativeProductEntityExt();
                    formatProduct.setParentId(item.getParentId());
                    formatProduct.setProductId(productId);
                    formatProduct.setRefId(item.getId());
                    formatProduct.setProductName(data.getString(ProductEnum.NAME.getAttr()));
                    formatProduct.setProductNum(productNum);
                    formatProduct.setBarcode(data.getString(ProductEnum.BARCODE.getAttr()));
                    String imageUrl = "";
                    JSONArray jsonArray = data.getJSONArray(ProductEnum.PRODUCT_IMGS.getAttr());
                    if (Objects.nonNull(jsonArray) && !jsonArray.isEmpty()){
                        imageUrl = jsonArray.getString(jsonArray.size()-1);
                    }
                    formatProduct.setImageUrl(imageUrl);
                    String specification = data.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr());
                    formatProduct.setProductSpecification(specificationModel.joinSpecification(specification));
                    formatProduct.setProductUnit(data.getString(ProductEnum.UNIT.getAttr()));
                    formatProduct.setMemo(item.getMemo());
                    Double price = attrDecimalPrecisionHelper.setPrecision(data.getDouble(ProductEnum.PRICE.getAttr()), amountPrecision);
                    formatProduct.setProductPrice(price);
                    // 产品单价存在，再计算合同订单的产品单价
                    *//*if (Objects.nonNull(price)) {
                        // 17823 【产品】修改产品单价，不应该同步修改系统已有合同订单的产品单价
                        try {
                            if (Double.compare(item.getDiscount(), 0) != 0) {
                                double oriPrice = productPrice * 100 / item.getDiscount();
                                oriPrice = attrDecimalPrecisionHelper.setPrecision(oriPrice, amountPrecision);
                                formatProduct.setProductPrice(oriPrice);
                            }
                        } catch (Exception e) {
                            LOG.info(e.getMessage());
                        }
                    }*//*

                    formatProduct.setProductNo(productNo);
                    Double stock = data.getDouble(ProductEnum.STOCK.getAttr()) == null ? 0 : data.getDouble(ProductEnum.STOCK.getAttr());
                    formatProduct.setStock(attrDecimalPrecisionHelper.setPrecision(stock, numberPrecision));
                    Double subTotal = productPrice * productNum;
                    // 小计
                    totalProductMoney = Arith.add(totalProductMoney, subTotal);
                    formatProduct.setSubTotal(subTotal);
                    formatProduct.setSaleSubtotal(attrDecimalPrecisionHelper.setPrecision(subTotal, amountPrecision));
                    Double contractProductNum = contractProductNumMap.get(productId);
                    if (alreadyOutstockProductMap.containsKey(productId)) {
                        Double outNum = alreadyOutstockProductMap.get(productId);
                        if (Double.compare(contractProductNum, outNum) <= 0) {
                            formatProduct.setStatus(I18nMessageUtil.getMessage(I18nStringConstant.OUT_STOCK));
                            formatProduct.setLackNum(0D);
                        } else {
                            formatProduct.setStatus(I18nMessageUtil.getMessage(I18nStringConstant.PARTIAL_DELIVERY));
                            Double lackNum = contractProductNum - outNum - stock;
                            formatProduct.setLackNum(lackNum < 0 ? 0 : attrDecimalPrecisionHelper.setPrecision(lackNum, numberPrecision));
                        }
                    } else {
                        formatProduct.setStatus(InOutStockStatusEnum.UN_OUTSTOCK.getName());
                        Double lackNum = contractProductNum - stock;
                        formatProduct.setLackNum(lackNum < 0 ? 0 : attrDecimalPrecisionHelper.setPrecision(lackNum, numberPrecision));
                    }
                    formatProduct.setSaleDiscount(item.getDiscount());
                    formatProduct.setSaleProductPrice(productPrice);
                    FormatRelativeProductDTO formatRelativeProductDTO = new FormatRelativeProductDTO();
                    BeanUtil.copyProperties(businessProductListDTO, formatRelativeProductDTO);
                    formatRelativeProductDTO.setBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
                    formatRelativeProductDTO.setTargetBusinessType(businessProductListDTO.getBusinessType());
                    formatRelativeProductDTO.setCorpid(corpid);
                    formatRelativeProductDTO.setNowProductExplainMap(nowProductExplainMap);
                    formatRelativeProductDTO.setFormatRelativeProductEntityExt(formatProduct);
                    formatRelativeProductDTO.setUserEntity(userEntity);
                    JSONObject productObj = productService.getFormatProductJsonArray(formatRelativeProductDTO);
                    productSortMap.put(item.getSort(), productObj);
                    JSONArray array = productObj.getJSONArray(StringConstant.RESULT);
                    boolean priceHide = false;
                    for (Object obj : array) {
                        JSONObject productInfo = (JSONObject) obj;
                        if (Objects.equals(productInfo.get("attr"), "productPrice")) {
                            priceHide = productInfo.getBoolean("hidePrice");
                        }
                    }

                    if (priceHide) {
                        totalProductMoneyHide = true;
                    }
                }
                *//*HandlerExplainDTO handlerExplainDTO = new HandlerExplainDTO();
                BeanUtil.copyProperties(businessProductListDTO, handlerExplainDTO);
                Boolean amountVisible = ExplainUtil.isFieldNeedHide(explainMap.get(ContractEnum.AMOUNT.getAttr()), handlerExplainDTO);
                Double amount =  contractData.getDouble(ContractEnum.AMOUNT.getAttr());
                String amountStr = attrDecimalPrecisionHelper.formatMoneyPrecision(amount,amountPrecision);
                othersObj.put("sumPrice", amountVisible == true ? "******" : amountStr);*//*
                // 暂无币种，写死合同单位
                othersObj.put("unit", I18nMessageUtil.getMessage(CommonConstant.YUAN));
                String totalMoney = totalProductMoneyHide ? StringConstant.INVISIBLE_VALUE : attrDecimalPrecisionHelper.formatMoneyPrecision(totalProductMoney, explainMap, ContractEnum.PRODUCT_TOTAL.getAttr());
                othersObj.put(BusinessConstant.TOTAL_PRODUCT_MONEY, totalMoney);
                businessProductListVO.setPageHelper(pageHelper);
            }
            if (!isOutstock) {
                List<ContractSurchargeEntity> surchargeList = getSurchargeList(corpid, contractId, amountPrecision);
                othersObj.put("surchargeList", surchargeList);
            }

            // 产品排序
            productArray.addAll(productSortMap.values());
            productFinalObj.put(StringConstant.PRODUCT_LIST, productArray);
            productFinalObj.put(BusinessConstant.OTHERS, othersObj);
            if (Objects.equals(businessProductListDTO.getSourceBusinessType(), XbbRefTypeEnum.REFUND.getCode())) {
                attrStr = RefundEnum.PRODUCTS.getAttr();
            }
            productFinalObj.put(StringConstant.REDUNDANT_FIELD_ATTR_BEGIN_FLAG, attrStr);

            if (businessProductListDTO.getDetailGet()) {
                List<FieldAttrEntityExt> fieldAttrEntityExtList = saasGetHelp.getSelectProductExplains(XbbRefTypeEnum.CONTRACT, businessProductListDTO.getBusinessType(), corpid, ContractEnum.PRODUCT.getAttr());
                for (FieldAttrEntityExt fieldAttrEntityExt : fieldAttrEntityExtList) {
                    String attr = fieldAttrEntityExt.getAttr();
                    if (Objects.equals(attr, SALE_PRODUCT_PRICE.getSaasAttr()) || Objects.equals(attr, SALE_DISCOUNT.getSaasAttr())) {
                        fieldAttrEntityExt.setShowSalePrice(1);
                    }
                }
                productFinalObj.put(BusinessConstant.EXPLAINS, fieldAttrEntityExtList);
            } else {
                ProductExplainDTO productExplainDTO = new ProductExplainDTO();
                BeanUtil.copyProperties(businessProductListDTO, productExplainDTO);
                productExplainDTO.setAttr(ContractEnum.PRODUCT.getAttr());
                productExplainDTO.setXbbRefTypeEnum(XbbRefTypeEnum.getByCode(businessProductListDTO.getSourceBusinessType()));
                productExplainDTO.setTargetCode(XbbRefTypeEnum.CONTRACT.getCode());
                productFinalObj.put(BusinessConstant.EXPLAINS, saasUpdateHelp.getSelectProductExplains(productExplainDTO));
            }
            businessProductListVO.setProduct(productFinalObj);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("contractServiceImpl.getContractProductList 出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return businessProductListVO;
    }*/

    @Override
    public void addSalePriceAndDiscountExplains(JSONObject result, OutstockProductEntityExt outstockProduct, PaasFormDataEntityExt product, Integer decimalPrecision, Integer showSalePriceFlag) {
        Double avgDiscount = outstockProduct.getDiscount();
        Double productNum = 0D;
        Double productPrice = 0D;

        if (avgDiscount != null) {
            // 老数据的折扣为null，新数据折扣不为null
            avgDiscount = Arith.div(avgDiscount, 100D);
            productNum = outstockProduct.getProductNum();
            // 此处的价格是售价，而不是单价
            productPrice = outstockProduct.getProductPrice();
        }
        JSONArray productExplains = result.getJSONArray("result");
        // 售价
        JSONObject productPriceJsonObj = new JSONObject();
        productPriceJsonObj.put("editable", 0);
        productPriceJsonObj.put("attr", OutstockSaleRelativeConstantEnum.PRODUCT_PRICE.getAlias());
        productPriceJsonObj.put("attrName", OutstockSaleRelativeConstantEnum.PRODUCT_PRICE.getName());
        setExplainsGeneralAttr(productPriceJsonObj, showSalePriceFlag);
        productPriceJsonObj.put("attrValue", avgDiscount == null ? "" : Arith.round(productPrice, decimalPrecision));
        productPriceJsonObj.put("decimalPrecision", decimalPrecision);
        productExplains.add(productPriceJsonObj);

        // 售价小计
        JSONObject subtotalJsonObj = new JSONObject();
        subtotalJsonObj.put("editable", 0);
        subtotalJsonObj.put("attr", OutstockSaleRelativeConstantEnum.SUBTOTAL.getAlias());
        subtotalJsonObj.put("attrName", OutstockSaleRelativeConstantEnum.SUBTOTAL.getName());
        setExplainsGeneralAttr(subtotalJsonObj, showSalePriceFlag);
        subtotalJsonObj.put("attrValue", avgDiscount == null ? "" : Arith.round(Arith.mul(productPrice, productNum), decimalPrecision));
        productPriceJsonObj.put("decimalPrecision", decimalPrecision);
        productExplains.add(subtotalJsonObj);

        // 单价
        JSONObject priceJsonObj = new JSONObject();
        priceJsonObj.put("editable", 0);
        priceJsonObj.put("attr", OutstockSaleRelativeConstantEnum.PRICE.getAlias());
        priceJsonObj.put("attrName", OutstockSaleRelativeConstantEnum.PRICE.getName());
        setExplainsGeneralAttr(priceJsonObj, showSalePriceFlag);
        productPriceJsonObj.put("decimalPrecision", decimalPrecision);
        Double salePrice;
        try {
            salePrice = product.getData().getDouble(ProductEnum.PRICE.getAttr());
        } catch (Exception e) {
            salePrice = 0D;
            LOG.error("销售出库-关联产品-获取售价出错", e);
        }
        priceJsonObj.put("attrValue", salePrice == null ? "" : Arith.round(salePrice, decimalPrecision));
        productExplains.add(priceJsonObj);

        // 折扣
        JSONObject discountJsonObj = new JSONObject();
        discountJsonObj.put("editable", 0);
        discountJsonObj.put("attr", OutstockSaleRelativeConstantEnum.DISCOUNT.getAlias());
        discountJsonObj.put("attrName", OutstockSaleRelativeConstantEnum.DISCOUNT.getName());
        setExplainsGeneralAttr(discountJsonObj, showSalePriceFlag);
        discountJsonObj.put("attrValue", avgDiscount == null ? "" : Arith.round(Arith.mul(avgDiscount, 100D), 2));
        productPriceJsonObj.put("decimalPrecision", 2);
        productExplains.add(discountJsonObj);
    }

    /**
     * @param corpid          公司id
     * @param contractId      合同id
     * @param amountPrecision
     * @return java.util.List<com.xbongbong.saas.domain.entity.ContractSurchargeEntity>
     * @throws XbbException 业务异常
     * @author 吴峰
     * @date 2019/4/12 10:48
     */
    private List<ContractSurchargeEntity> getSurchargeList(String corpid, Long contractId, Integer amountPrecision) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(StringConstant.CORPID, corpid);
        param.put("contractId", contractId);
        param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
        List<ContractSurchargeEntity> surchargeList = contractSurchargeModel.findEntitys(param);
        if (Objects.isNull(surchargeList)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        for (ContractSurchargeEntity contractSurchargeEntity : surchargeList) {
            Double surchargeAmount = contractSurchargeEntity.getAmount();
            surchargeAmount = attrDecimalPrecisionHelper.setPrecision(surchargeAmount, amountPrecision);
            contractSurchargeEntity.setAmount(surchargeAmount);
        }
        return surchargeList;
    }


    /**
     * @description: 获取合同已经出库的产品，并将相同的产品出库数量叠加
     * @author: GuoJun.HU
     * @date: 2017/12/8 14:45
     * @modify:
     * @version:
     * @return: key:产品id value:出库数量
     */
    public Map<Long, Double> getAlreadyOutstockProductMap(ContractEntityExt contract) throws XbbException {
        Map<Long, Double> outstockProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        /*JSONObject contractData = contract.getData();
        if (Objects.isNull(contractData)) {
            return outstockProductMap;
        }
        JSONArray outstockIdArray = contractData.getJSONArray(ContractEnum.OUTSTOCK_ID.getAttr());
        if (Objects.isNull(outstockIdArray)) {
            outstockIdArray = new JSONArray();
        }*/
        // 合同是否出库，outstock_id不一定正确，因为迁移数据里，已出库合同的outstock_id不一定存了出库单，所以直接查出库单是否关联合同
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", contract.getCorpid()));
        boolQueryBuilder.filter(termQuery(OutstockEnum.getEsAttr4Keyword(OutstockEnum.TYPE), OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
        boolQueryBuilder.filter(termQuery(OutstockEnum.getEsAttr4Keyword(OutstockEnum.REF_ID), contract.getId()));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        List<String> fieldList = new ArrayList<>();
        fieldList.add(FieldTypeEnum.DATAID.getAlias());
        List<PaasFormDataEntityExt> outstockList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_OUTSTOCK, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        if (CollectionsUtil.isEmpty(outstockList)) {
            return outstockProductMap;
        }
        List<Long> outstockIds = new ArrayList<>(outstockList.size());
        outstockList.forEach(item -> outstockIds.add(item.getDataId()));

        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", contract.getCorpid());
        param.put("del", 0);
        param.put("outstockIdIn", outstockIds);
        List<OutstockProductEntity> outstockProductList = outstockProductModel.findEntitys(param);

        for (OutstockProductEntity entity : outstockProductList) {
            Long productId = entity.getProductId();
            Double productNum = entity.getProductNum();
            if (outstockProductMap.containsKey(productId)) {
                Double mergeNum = outstockProductMap.get(productId) + productNum;
                outstockProductMap.put(productId, mergeNum);
            } else {
                outstockProductMap.put(productId, productNum);
            }
        }
        return outstockProductMap;
    }

    /**
     * @description: 获取合同关联产品，并将相同的产品数量合并
     * @author: GuoJun.HU
     * @date: 2017/12/8 14:56
     * @modify:
     * @version:
     * @return: key:产品id value:产品数量
     */
    public Map<Long, Double> getContractProductNumMap(List<ContractProductEntity> contractProducts) {
        Map<Long, Double> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ContractProductEntity entity : contractProducts) {
            Long productId = entity.getProductId();
            Double productNum = entity.getProductNum();
            if (contractProductMap.containsKey(productId)) {
                Double mergeNum = contractProductMap.get(productId) + productNum;
                contractProductMap.put(productId, mergeNum);
            } else {
                contractProductMap.put(productId, productNum);
            }
        }
        return contractProductMap;
    }


    public void setExplainsGeneralAttr(JSONObject productPriceJsonObj, Integer showSalePriceFlag) {
        productPriceJsonObj.put("isRedundant", 0);
        // 增加开关标志
        productPriceJsonObj.put("showSalePrice", showSalePriceFlag);
    }

    @Override
    public FormDataAddDTO formatExcelValue(ImportFormDataDTO importFormDataDTO, List<CellTitlePojo> titlePojoList, RowGroupPojo rowGroupPojo, List<FieldAttrEntityForImport> fieldAttrList) throws XbbException {
        FormDataAddDTO formDataAddDTO = new FormDataAddDTO();
        BeanUtil.copyProperties(importFormDataDTO, formDataAddDTO);
        formDataAddDTO.setSerialNo("");
        JSONObject dataJson = new JSONObject();
        List<List<CellValuePojo>> rowValueList = rowGroupPojo.getRowValueList();
        // 分组的第一条数据作为给实体对象赋值的数据
        List<CellValuePojo> cellValueList = rowValueList.get(0);

        FieldAttrEntityForImport linkCustomerAttr = null;
        for (FieldAttrEntityForImport fieldAttrEntityForImport : fieldAttrList) {
            if (Objects.equals(fieldAttrEntityForImport.getAttr(), ContractEnum.LINK_CUSTOMER.getAttr())) {
                linkCustomerAttr = fieldAttrEntityForImport;
                break;
            }
        }

        Long linkCustomerId = 0L;
        if (Objects.nonNull(linkCustomerAttr)) {
            linkCustomerId = importHelper.formatLinkCustomer2CustomerId(titlePojoList, dataJson, rowValueList, linkCustomerAttr);
        }

        ReturnUserAndDepartmentPojo returnUserAndDepartmentPojo = new ReturnUserAndDepartmentPojo();
        Map<String, FieldAttrEntity> explainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        String corpid = importFormDataDTO.getCorpid();
        for (FieldAttrEntityForImport fieldAttr : fieldAttrList) {
            String attr = fieldAttr.getAttr();
            Integer fieldType = fieldAttr.getFieldType();
            String attrName = fieldAttr.getAttrName();
            Integer columnIndex = importHelper.getColumnIndexByTitle(titlePojoList, attrName, null);
            Object cellValue = importHelper.getValueByColumnIndex(cellValueList, columnIndex);
            if (Objects.equals(attr, ContractEnum.PRODUCT.getAttr())) {
                importHelper.formatCommonProduct(titlePojoList, dataJson, rowValueList, fieldAttr, XbbRefTypeEnum.CONTRACT.getCode(), corpid);
            } else if (Objects.equals(attr, ContractEnum.LINK_CUSTOMER.getAttr())) {

            } else if (Objects.equals(fieldType, FieldTypeEnum.SUB_FORM.getType())) {
                importHelper.formatSubFormValue4Import(rowValueList, titlePojoList, importFormDataDTO, fieldAttr, dataJson);
            } else {
                if (Objects.isNull(cellValue) || StringUtil.isEmpty(cellValue.toString())) {
                    continue;
                }
                // 签订人特殊处理
                if (Objects.equals(attr, ContractEnum.SIGN_PERSON.getAttr())) {
                    Map<String, Object> linkBusinessMap = fieldAttr.getLinkBusinessMap();
                    if (linkBusinessMap.containsKey(cellValue.toString())) {
                        returnUserAndDepartmentPojo.setId(linkBusinessMap.get(cellValue.toString()));
                    } else {
                        // 签订人错误数据id用-1
                        if (Objects.equals(importFormDataDTO.getIsNoSignImport(), 1)) {
                            returnUserAndDepartmentPojo.setId(StringConstant.NOT_IN_SYSTEM_ID);
                            returnUserAndDepartmentPojo.setName(cellValue.toString());
                        } else {
                            throw new XbbException(ImportErrorCodeEnum.API_ERROR_235013);
                        }
                    }
                    dataJson.put(attr, JSON.toJSON(returnUserAndDepartmentPojo));
                } else if (ImportHelper.commonHandleFieldType.contains(fieldAttr.getFieldType())) {
                    importHelper.formatCommonField(dataJson, fieldAttr, cellValue);
                } else {
                    if (Objects.equals(attr, ContractEnum.LINK_CONTACT.getAttr())) {
                        importHelper.formatRelyBusiness(linkCustomerId, dataJson, fieldAttr, cellValue, ContactEnum.CUSTOMER_NAME.getAttr());
                    } else if (Objects.equals(attr, ContractEnum.LINK_OPPORTUNITY.getAttr())) {
                        importHelper.formatRelyBusiness(linkCustomerId, dataJson, fieldAttr, cellValue, SalesOpportunityEnum.CUSTOMER_NAME.getAttr());
                    } else if (Objects.equals(attr, ContractEnum.QUOTATION_ID.getAttr())) {
                        importHelper.formatRelyBusiness(linkCustomerId, dataJson, fieldAttr, cellValue, QuotationEnum.CUSTOMER_ID.getAttr());
                    } else if (Objects.equals(fieldType, FieldTypeEnum.SERIALNO.getType())) {
                        formDataAddDTO.setSerialNo(cellValue.toString());
                    } else if (Objects.equals(fieldType, FieldTypeEnum.LINK_DATA.getType())) {
                        importHelper.formatLinkData(cellValue, fieldAttr, dataJson);
                    } else if (Objects.equals(fieldType, FieldTypeEnum.LINK_DATA_MULTI.getType())) {
                        importHelper.formatLinkDataMulti(cellValue, importFormDataDTO, fieldAttr, dataJson);
                    } else {
                        dataJson.put(attr, cellValue);
                    }
                }
            }
            if (Objects.equals(attr, ContractEnum.ADD_PAYMENT.getAttr())) {
                if (Objects.nonNull(fieldAttr.getSubForm()) && Objects.nonNull(fieldAttr.getSubForm().getItems())) {
                    explainMap = ExplainUtil.getExplainMapByList(fieldAttr.getSubForm().getItems());
                }
            }
        }
        JSONArray paymentArr = dataJson.getJSONArray(ContractEnum.ADD_PAYMENT.getAttr());
        List<String> canCoverStatusList = Arrays.asList(PaymentStatusEnum.RECEIVABLES.getCode(), PaymentStatusEnum.UN_RECEIVABLES.getCode());
        if (Objects.nonNull(paymentArr) && !paymentArr.isEmpty()) {
            for (int i = 0; i < paymentArr.size(); i++) {
                JSONObject paymentObj = paymentArr.getJSONObject(i).getJSONObject(StringConstant.JSON_DATA);
                if (!canCoverStatusList.contains(paymentObj.getString(PaymentEnum.STATUS.getAttr()))) {
                    throw new XbbException(ImportErrorCodeEnum.API_ERROR_235019);
                }
                if (Objects.equals(paymentObj.getString(PaymentEnum.STATUS.getAttr()), PaymentStatusEnum.RECEIVABLES.getCode())) {
                    paymentObj.put(PaymentEnum.BELONGER.getAttr(), returnUserAndDepartmentPojo);
                }
                if (Objects.nonNull(paymentObj.get(FieldTypeEnum.DEPARTMENTID.getAlias()))) {
                    Long departmentId = departmentService.verifyDepartment(importFormDataDTO, explainMap, paymentObj);
                    paymentArr.getJSONObject(i).put(FieldTypeEnum.DEPARTMENTID.getAlias(), departmentId);
                    paymentObj.put(FieldTypeEnum.DEPARTMENTID.getAlias(), new ReturnUserAndDepartmentPojo(departmentId));
                }
            }
        }
        dataJson.put(ContractEnum.ADD_PAYMENT.getAttr(), paymentArr);
        formDataAddDTO.setDataList(dataJson);
        return formDataAddDTO;
    }

    /**
     * 获取某个合同关联的产品
     *
     * @param corpid     公司id
     * @param contractId 合同id
     * @return
     * @throws XbbException
     */
    @Override
    public List<ContractProductEntity> getContractProductList(String corpid, Long contractId) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("contractId", contractId);
        param.put("del", 0);
        param.put("orderByStr", "CONVERT(product_name USING gbk) ASC");
        List<ContractProductEntity> contractProductList = contractProductModel.findEntitys(param);
        return contractProductList;
    }

    @Override
    public void updateCashProfit(ContractUpdateCashProfitDTO updateCashProfitDTO) throws XbbException {
        try {
            CashProfitSavePojo cashProfitSavePojo = initCashProfitPojo(updateCashProfitDTO);
            CashProfitReturnVO cashProfitReturnPojo = getCashProfit(cashProfitSavePojo);
            ContractUpdateBatchDTO contractUpdateBatchDTO = new ContractUpdateBatchDTO();
            BeanUtil.copyProperties(updateCashProfitDTO, contractUpdateBatchDTO);
            List<ContractUpdateDTO> contractUpdateList = new ArrayList<>();
            ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
            contractUpdateDTO.setId(updateCashProfitDTO.getContractEntityExt().getId());
            JSONObject data = new JSONObject();
            data.put(ContractEnum.CASH_PROFIT.getAttr(), cashProfitReturnPojo.getCashProfit());
            data.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), cashProfitReturnPojo.getCashProfitRate());
            contractUpdateDTO.setData(data);
            contractUpdateList.add(contractUpdateDTO);
            contractUpdateBatchDTO.setContractUpdateList(contractUpdateList);
            updateBatch(contractUpdateBatchDTO);
        } catch (Exception e) {
            LOG.error("contractService.updateCashProfit报错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    @Override
    public void restoreNewContractOutboundAndStatus(ContractRestoreOutboundDTO contractRestoreOutboundDTO) throws XbbException {
        try {
            List<Long> delOutstockIdList = contractRestoreOutboundDTO.getDelOutstockIdList();
            List<OutstockEntityExt> oldContractOutstockList = contractRestoreOutboundDTO.getOldContractOutstockList();
            List<OutstockEntityExt> newContractOutstockList = contractRestoreOutboundDTO.getNewContractOutstockList();
            List<Long> oldContractIdList = new ArrayList<>();
            List<Long> newContractIdList = new ArrayList<>();
            Integer distributorMark = contractRestoreOutboundDTO.getDistributorMark();
            oldContractOutstockList.forEach(item -> {
                JSONObject data = item.getData();
                if (Objects.nonNull(data) && Objects.nonNull(data.get(OutstockEnum.REF_ID.getAttr())) && !Objects.equals(data.getLong(OutstockEnum.REF_ID.getAttr()), 0D)) {
                    oldContractIdList.add(data.getLong(OutstockEnum.REF_ID.getAttr()));
                }
            });
            newContractOutstockList.forEach(item -> {
                JSONObject data = item.getData();
                if (Objects.nonNull(data) && Objects.nonNull(data.get(OutstockEnum.REF_ID.getAttr())) && !Objects.equals(data.getLong(OutstockEnum.REF_ID.getAttr()), 0D)) {
                    newContractIdList.add(data.getLong(OutstockEnum.REF_ID.getAttr()));
                }
            });
            List<Long> contractIdList = new ArrayList<>();
            contractIdList.addAll(oldContractIdList);
            contractIdList.addAll(newContractIdList);

            String corpid = contractRestoreOutboundDTO.getCorpid();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("idIn", contractIdList);
            List<ContractEntityExt> contractEntityList = contractModel.findEntitys(param);

            List<ContractEntityExt> oldContractList = new ArrayList<>();
            List<ContractEntityExt> newContractList = new ArrayList<>();
            for (ContractEntityExt contractEntityExt : contractEntityList) {
                if (oldContractIdList.contains(contractEntityExt.getId())) {
                    oldContractList.add(contractEntityExt);
                } else if (newContractIdList.contains(contractEntityExt.getId())) {
                    newContractList.add(contractEntityExt);
                }
            }

            ContractOutstockDeleteVO contractOutstockDeleteVO = calculateContractCostBatch(corpid, contractIdList, contractEntityList, delOutstockIdList);
            Map<Long, List<Long>> contractOutstockMap = contractOutstockDeleteVO.getContractOutstockMap();
            List<ContractUpdateDTO> contractUpdateList = contractOutstockDeleteVO.getContractUpdateList();
            Map<Long, ContractUpdateDTO> contractUpdateMap = new HashMap<>();
            contractUpdateList.forEach(contractUpdateDTO -> contractUpdateMap.put(contractUpdateDTO.getId(), contractUpdateDTO));

            ContractProductUpdateOutstockNumVO newContractProductUpdateOutstockNumVO = saasProductHelp.recalculateNewOutstockNum(newContractList, contractOutstockDeleteVO);
            ContractProductUpdateOutstockNumVO oldContractProductUpdateOutstockNumVO = saasProductHelp.recalculateOutstockNum(oldContractList, contractOutstockDeleteVO);
            List<Long> oldNeedUpdateOutboundStatusContractList = oldContractProductUpdateOutstockNumVO.getNeedUpdateOutboundStatusContractList();
            List<Long> newNeedUpdateOutboundStatusContractList = newContractProductUpdateOutstockNumVO.getNeedUpdateOutboundStatusContractList();

            List<ContractProductEntity> oldContractProductUpdateOutstockNumList = oldContractProductUpdateOutstockNumVO.getContractProductUpdateOutstockNumList();
            List<ContractProductEntity> newContractProductUpdateOutstockNumList = newContractProductUpdateOutstockNumVO.getContractProductUpdateOutstockNumList();
            List<ContractProductEntity> contractProductUpdateOutstockNumList = new ArrayList<>();
            contractProductUpdateOutstockNumList.addAll(oldContractProductUpdateOutstockNumList);
            contractProductUpdateOutstockNumList.addAll(newContractProductUpdateOutstockNumList);
            if (!contractProductUpdateOutstockNumList.isEmpty()) {
                // 更新合同产品已出库数量
                contractProductModel.updateBatchImmediately(contractProductUpdateOutstockNumList, corpid);
            }
            List<Long> writeOffContractIds = new ArrayList<>();
            for (ContractEntityExt contractEntity : contractEntityList) {
                Long contractId = contractEntity.getId();
                ContractUpdateDTO contractUpdateDTO = contractUpdateMap.get(contractId);
                if (Objects.isNull(contractUpdateDTO)) {
                    continue;
                }
                JSONObject contractData = contractUpdateDTO.getData();

                // 更新合同关联的出库单id
                List<Long> outstockIdList;
                if (contractOutstockMap.containsKey(contractId)) {
                    outstockIdList = contractOutstockMap.get(contractId);
                } else {
                    String outstockIdListStr = FastJsonHelper.getStringOrDefaultFromFormData(contractEntity.getData(), ContractEnum.OUTSTOCK_ID.getAttr(), new JSONArray().toJSONString());
                    outstockIdList = JSON.parseArray(outstockIdListStr, Long.class);
                    outstockIdList.removeAll(delOutstockIdList);
                }
                contractData.put(ContractEnum.OUTSTOCK_ID.getAttr(), JSON.parseArray(JSON.toJSONString(outstockIdList)));
                String shipStatus = FastJsonHelper.getStringOrDefaultFromFormData(contractData, ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.CLOSED.getCode());
                if (!Objects.equals(shipStatus, ShipStatusEnum.CLOSED.getAttr())) {
                    contractData.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.DELIVERED.getCode());
                    if (outstockIdList.isEmpty()) {
                        contractData.put(ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.UNSHIPPED.getCode());
                    }
                }

                boolean isNeedUpdateOutboundStatus = false;
                if (oldContractIdList.contains(contractId)) {
                    isNeedUpdateOutboundStatus = oldNeedUpdateOutboundStatusContractList.contains(contractId);
                } else if (newContractIdList.contains(contractId)) {
                    isNeedUpdateOutboundStatus = newNeedUpdateOutboundStatusContractList.contains(contractId);
                }
                // 删除出库发货单 更新订货单订单状态的出库状态发货状态和是否出库完毕标志
                if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                    // 删除出库发货单  订单状态一定为待发货
                    contractData.put(OrderEnum.STATUS.getAttr(), OrderStatusEnum.DELIVERED.getCode());
                    BoolQueryBuilder boolQueryBuilder = boolQuery();
                    boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                    boolQueryBuilder.filter(termQuery(ParameterConstant.DEL, DelEnum.NORMAL.getDel()));
                    boolQueryBuilder.filter(termQuery(OrderOutStockEnum.getAttrConnectData(OrderOutStockEnum.REF_ID), contractId));
                    // 该订货单下 是否有其他出库发货单 有其他出库发货单 订单单的发货状态为部分发货 否则为待发货
                    long orderOutStockCount = paasEsModel.count(boolQueryBuilder, IndexTypeEnum.IDX_SAAS_OUTSTOCK);
                    if (orderOutStockCount > BasicConstant.ZERO) {
                        contractData.put(OrderEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.DELIVERED.getCode());
                    } else {
                        contractData.put(OrderEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.UNSHIPPED.getCode());
                    }
                    boolQueryBuilder.mustNot(termQuery(OrderOutStockEnum.getEsAttr4Keyword(OrderOutStockEnum.EXPRESS_STATUS), OrderOutStockExpressStatusEnum.TO_BE_DELIVERED.getCode()));
                    // 出库发货单发货状态不等于待发货的单据大于0个 说明有发货的出库发货单  则订单状态更新为部分发货
                    long count = paasEsModel.count(boolQueryBuilder, IndexTypeEnum.IDX_SAAS_OUTSTOCK);
                    if (count > BasicConstant.ZERO) {
                        contractData.put(OrderEnum.EXPRESS_STATUS.getAttr(), OrderExpressStatusEnum.PARTIAL_DELIVERY.getCode());
                    } else {
                        contractData.put(OrderEnum.EXPRESS_STATUS.getAttr(), OrderExpressStatusEnum.DELIVERED.getCode());
                    }
                    // 删除出库发货单 更新订货单的是否出库完毕标示为否
                    contractData.put(OrderEnum.ALL_OUT_BOUND.getAttr(), BasicConstant.ZERO);
                } else {
                    if (isNeedUpdateOutboundStatus) {
                        contractData.put(ContractEnum.ALL_OUT_BOUND.getAttr(), 0);
                        contractData.put(ContractEnum.STATUS.getAttr(), 2);
                    }
                }
            }
            if (!contractUpdateList.isEmpty()) {
                ContractUpdateBatchDTO contractUpdateBatchDTO = new ContractUpdateBatchDTO();
                BeanUtil.copyProperties(contractRestoreOutboundDTO, contractUpdateBatchDTO);
                contractUpdateBatchDTO.setContractUpdateList(contractUpdateList);
                updateBatchImmediately(contractUpdateBatchDTO);
            }
            if (CollectionUtils.isNotEmpty(writeOffContractIds)) {
                writeOffRecordService.updateOrderIdsStatus(OrderStatusEnum.RECEIVED.getCode(), writeOffContractIds, corpid);
            }
        } catch (XbbException e) {
            LOG.error("contractService.restoreNewContractOutboundAndStatus 报错：", e);
            throw e;
        } catch (Exception e) {
            LOG.error("contractService.restoreNewContractOutboundAndStatus 报错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    @Override
    public ListAppVO formatWaitContract(FormDataListDTO formDataListDTO, PaasFormDataEsListVO formDataListVO) throws XbbException {
        List<AppListPojo> appListPojos = new ArrayList<>();
        String corpid = formDataListDTO.getCorpid();
        List<PaasFormDataEntityExt> formDataList = formDataListVO.getPaasFormDataESList();
        if (formDataList.isEmpty()) {
            return new ListAppVO();
        }
        Integer isProduce = 0;
        if (Objects.equals(formDataListDTO.getSubBusinessType(), XbbRefTypeEnum.WAIT_PURCHASE.getCode())) {
            isProduce = ContractProduceEnum.WAIT_PURCHASE.getCode();
        } else {
            isProduce = ContractProduceEnum.WAIT_PRODUCE.getCode();
        }
        List<Long> dataIdList = new ArrayList<>();
        dataIdList.add(-1L);
        formDataList.forEach(item -> dataIdList.add(item.getId()));
        Set<Long> productIdSet = new HashSet<>();
        Map<String, Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        map.put("columns", "any_value(contract_id) as contract_id,any_value(product_id) as product_id,sum(product_num) as product_num,sum(outstock_num) as outstock_num");
        map.put("corpid", corpid);
        map.put("isProduce", isProduce);
        map.put("del", 0);
        map.put("contractIdIn", dataIdList);
        map.put("groupByStr", "contract_id,product_id");
        //获取合同产品信息
        List<ContractProductEntity> contractProductEntityList = contractProductModel.findEntitys(map);
        Map<Long, List<ContractProductEntity>> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ContractProductEntity entity : contractProductEntityList) {
            Double proNum = entity.getProductNum() == null ? 0D : Math.abs(entity.getProductNum());
            if (Objects.equals(proNum, entity.getOutstockNum())) {
                continue;
            }
            List<ContractProductEntity> singleContractProductList = new ArrayList<>();
            singleContractProductList.add(entity);
            if (contractProductMap.containsKey(entity.getContractId())) {
                singleContractProductList.addAll(contractProductMap.get(entity.getContractId()));
            }
            contractProductMap.put(entity.getContractId(), singleContractProductList);
            productIdSet.add(entity.getProductId());
        }
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(), productIdSet));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_PRODUCT.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_PRODUCT.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 0, productIdSet.size());
        searchRequest.source(sourceBuilder);
        //关联产品的具体信息
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt entity : esEntities.getContent()) {
            productMap.put(entity.getDataId(), entity);
        }
        Map<Long, List<WaitContractProductPojo>> waitProductPojoMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (Map.Entry<Long, List<ContractProductEntity>> entry : contractProductMap.entrySet()) {
            List<ContractProductEntity> productEntityList = entry.getValue();
            Long contractId = entry.getKey();
            List<WaitContractProductPojo> waitContractProductPojoList = waitProductPojoMap.getOrDefault(contractId, new ArrayList<>());
            for (ContractProductEntity contractProductEntity : productEntityList) {
                Long productId = contractProductEntity.getProductId();
                if (!productMap.containsKey(productId)) {
                    continue;
                }
                JSONObject productData = productMap.get(productId).getData();
                Double stock = productData.getDouble(ProductEnum.STOCK.getAttr());
                stock = Objects.isNull(stock) ? 0D : stock;
                Double shortageQuantity = contractProductEntity.getProductNum() - contractProductEntity.getOutstockNum() - stock;
                if (shortageQuantity <= 0) {
                    continue;
                }
                WaitContractProductPojo waitContractProductPojo = new WaitContractProductPojo();
                waitContractProductPojo.setProductNum(shortageQuantity);
                waitContractProductPojoList.add(waitContractProductPojo);
            }

            waitProductPojoMap.put(contractId, waitContractProductPojoList);
        }
        Map<Long, String> mainUserMap = userTeamService.getMainUserMap(dataIdList, corpid, XbbRefTypeEnum.CONTRACT.getCode(), false);
        for (PaasFormDataEntityExt contract : formDataList) {
            JSONObject data = contract.getData();
            Long contractId = contract.getId();
            String contractName = FastJsonHelper.getStringOrDefaultFromFormData(data, ContractEnum.NAME.getAttr(), "");
            JSONArray explains = new JSONArray();
            String customerName = "";
            JSONArray customerArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, ContractEnum.LINK_CUSTOMER.getAttr(), new JSONArray());
            if (!customerArr.isEmpty()) {
                JSONObject customerObj = (JSONObject) customerArr.get(0);
                customerName = customerObj.getString(StringConstant.SAAS_LINK_BUSINESS_NAME);
            }
            JSONObject others = new JSONObject();
            List<WaitContractProductPojo> waitContractProductPojoList = waitProductPojoMap.getOrDefault(contractId, new ArrayList<>());
            others.put("productPurchased", waitContractProductPojoList.size());
            others.put("formId", contract.getFormId());
            others.put("menuId", contract.getMenuId());
            others.put(StringConstant.BUSINESS_TYPE, XbbRefTypeEnum.CONTRACT.getCode());
            others.put(StringConstant.DATA_ID, contractId);
            others.put("sourceBusinessType", XbbRefTypeEnum.PRODUCT.getCode());
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.CONTRACT_NO.getAttrName(), contract.getSerialNo()));
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.LINK_CUSTOMER.getAttrName(), customerName));
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.OWNER_ID.getAttrName(), mainUserMap.getOrDefault(contractId, "")));
            appListPojos.add(new AppListPojo(contract.getId(), contractName, explains, null, null, others));
        }
        ListAppVO listAppVO = new ListAppVO();
        listAppVO.setList(appListPojos);
        listAppVO.setTotalCount(formDataListVO.getPageHelper().getRowsCount());
        return listAppVO;
    }

    @Override
    public ListAppVO formatWaitPurchaseContract(FormDataListDTO formDataListDTO, PaasFormDataEsListVO formDataListVO) throws XbbException {
        List<AppListPojo> appListPojos = new ArrayList<>();
        String corpid = formDataListDTO.getCorpid();
        List<PaasFormDataEntityExt> formDataList = formDataListVO.getPaasFormDataESList();
        if (formDataList.isEmpty()) {
            return new ListAppVO();
        }
        Integer isProduce = 0;
        if (Objects.equals(formDataListDTO.getSubBusinessType(), XbbRefTypeEnum.WAIT_PURCHASE.getCode())) {
            isProduce = ContractProduceEnum.WAIT_PURCHASE.getCode();
        } else {
            isProduce = ContractProduceEnum.WAIT_PRODUCE.getCode();
        }
        List<Long> dataIdList = new ArrayList<>();
        dataIdList.add(-1L);
        formDataList.forEach(item -> dataIdList.add(item.getDataId()));
        Set<Long> productIdSet = new HashSet<>();
        Map<String, Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        map.put("columns", "any_value(contract_id) as contract_id,any_value(product_id) as product_id,sum(product_num) as product_num,sum(outstock_num) as outstock_num");
        map.put("corpid", corpid);
        map.put("isProduce", isProduce);
        map.put("del", 0);
        map.put("contractIdIn", dataIdList);
        map.put("groupByStr", "contract_id,product_id");
        //获取合同产品信息
        List<ContractProductEntity> contractProductEntityList = contractProductModel.findEntitys(map);
        Map<Long, List<ContractProductEntity>> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ContractProductEntity entity : contractProductEntityList) {
            Double proNum = entity.getProductNum() == null ? 0D : Math.abs(entity.getProductNum());
            if (Objects.equals(proNum, entity.getOutstockNum())) {
                continue;
            }
            List<ContractProductEntity> singleContractProductList = new ArrayList<>();
            singleContractProductList.add(entity);
            if (contractProductMap.containsKey(entity.getContractId())) {
                singleContractProductList.addAll(contractProductMap.get(entity.getContractId()));
            }
            contractProductMap.put(entity.getContractId(), singleContractProductList);
            productIdSet.add(entity.getProductId());
        }
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(), productIdSet));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_PRODUCT.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_PRODUCT.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 0, productIdSet.size());
        searchRequest.source(sourceBuilder);
        //关联产品的具体信息
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt entity : esEntities.getContent()) {
            productMap.put(entity.getDataId(), entity);
        }
        LinkedHashMap<Long, List<WaitContractProductPojo>> waitProductPojoMap = new LinkedHashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        saasListHelp.getNewWaitContractProductPojoMap(waitProductPojoMap,formDataListDTO,corpid,new ArrayList<>());
        Map<Long, String> mainUserMap = userTeamService.getMainUserMap(dataIdList, corpid, XbbRefTypeEnum.CONTRACT.getCode(), false);
        for (PaasFormDataEntityExt contract : formDataList) {
            JSONObject data = contract.getData();
            Long contractId = contract.getDataId();
            String contractName = FastJsonHelper.getStringOrDefaultFromFormData(data, ContractEnum.NAME.getAttr(), "");
            JSONArray explains = new JSONArray();
            String customerName = "";
            JSONArray customerArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, ContractEnum.LINK_CUSTOMER.getAttr(), new JSONArray());
            if (!customerArr.isEmpty()) {
                JSONObject customerObj = (JSONObject) customerArr.get(0);
                customerName = customerObj.getString(StringConstant.SAAS_LINK_BUSINESS_NAME);
            }
            JSONObject others = new JSONObject();
            List<WaitContractProductPojo> waitContractProductPojoList = waitProductPojoMap.getOrDefault(contractId, new ArrayList<>());
            others.put("productPurchased", waitContractProductPojoList.size());
            others.put("formId", contract.getFormId());
            others.put("menuId", contract.getMenuId());
            others.put(StringConstant.BUSINESS_TYPE, XbbRefTypeEnum.CONTRACT.getCode());
            others.put(StringConstant.DATA_ID, contractId);
            others.put("sourceBusinessType", XbbRefTypeEnum.PRODUCT.getCode());
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.CONTRACT_NO.getAttrName(), contract.getData().getString(ContractEnum.CONTRACT_NO.getAttr())));
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.LINK_CUSTOMER.getAttrName(), customerName));
            String mainUser = mainUserMap.getOrDefault(contractId, "");
            if (Objects.isNull(data.get(ContractEnum.OWNER_ID.getAttr()))){
                mainUser = "";
            }
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.OWNER_ID.getAttrName(),mainUser));
            appListPojos.add(new AppListPojo(contract.getDataId(), contractName, explains, null, null, others));
        }
        ListAppVO listAppVO = new ListAppVO();
        listAppVO.setList(appListPojos);
        listAppVO.setTotalCount(formDataListVO.getPageHelper().getRowsCount());
        return listAppVO;
    }

    /**
     * 获取移动端待入库中的合同
     *
     * @param formDataListDTO 列表查询DTO
     * @param formDataListVO  列表查询回参
     * @throws XbbException
     * @author youli.chen
     * @date 2019-05-31 11:03
     */
    @Override
    public ListAppVO formatWaitPurchaseInstock(FormDataListDTO formDataListDTO, PaasFormDataEsListVO formDataListVO) throws XbbException {
        List<AppListPojo> appListPojos = new ArrayList<>();
        String corpid = formDataListDTO.getCorpid();
        List<PaasFormDataEntityExt> formDataList = formDataListVO.getPaasFormDataESList();
        if (formDataList.isEmpty()) {
            return new ListAppVO();
        }
        List<Long> dataIdList = new ArrayList<>();
        formDataList.forEach(item -> dataIdList.add(item.getId()));
        Set<Long> productIdSet = new HashSet<>();
        Map<String, Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        map.put("columns", "any_value(purchase_sheet_id) as purchase_sheet_id,any_value(product_id) as product_id,sum(product_num) as product_num");
        map.put("corpid", corpid);
        map.put("del", DelEnum.NORMAL.getDel());
        map.put("purchaseIdIn", dataIdList);
        //map.put("groupByStr", "purchase_sheet_id,product_id");
        //获取合同产品信息
        List<PurchaseProductEntity> purchaseProductEntityList = purchaseProductModel.findEntitys(map);
        //获得采购入库单产品信息
        Map<String, Object> instockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        instockMap.put("corpid", corpid);
        instockMap.put("del", DelEnum.NORMAL.getDel());
        instockMap.put("purchaseIdList", dataIdList);
        List<InstockProductEntity> instockProductEntityList = instockProductModel.getInstockProductByRefId(instockMap);
        Map<Long, Double> instockProduct = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        instockProductEntityList.forEach(instockProductEntity -> {
            Double num = instockProduct.get(instockProductEntity.getRefProductId());
            if(Objects.nonNull(num)){
                num = num + instockProductEntity.getProductNum();
            }else {
                num = instockProductEntity.getProductNum();
            }
            instockProduct.put(instockProductEntity.getRefProductId(), num);
        });
        //减去入库了的产品
        purchaseProductEntityList.forEach(purchaseProductEntity -> {
            Double num = instockProduct.get(purchaseProductEntity.getId());
            if (Objects.nonNull(num)){
                purchaseProductEntity.setInstockNum(num);
            }
        });
        Map<Long, List<PurchaseProductEntity>> purchaseProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PurchaseProductEntity entity : purchaseProductEntityList) {
            List<PurchaseProductEntity> singlePurchaseProductList = new ArrayList<>();
            singlePurchaseProductList.add(entity);
            if (purchaseProductMap.containsKey(entity.getPurchaseSheetId())) {
                singlePurchaseProductList.addAll(purchaseProductMap.get(entity.getPurchaseSheetId()));
            }
            purchaseProductMap.put(entity.getPurchaseSheetId(), singlePurchaseProductList);
            productIdSet.add(entity.getProductId());
        }
        Map<Long, List<WaitOutstockProductPojo>> waitOutProductPojoMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (Map.Entry<Long, List<PurchaseProductEntity>> entry : purchaseProductMap.entrySet()) {
            List<PurchaseProductEntity> productEntityList = entry.getValue();
            Long purchaseId = entry.getKey();
            List<WaitOutstockProductPojo> waitPurchaseProductPojoList = waitOutProductPojoMap.getOrDefault(purchaseId, new ArrayList<>());
            for (PurchaseProductEntity purchaseProductEntity : productEntityList) {
                Double shortageQuantity = purchaseProductEntity.getProductNum() - purchaseProductEntity.getInstockNum();
                if (shortageQuantity <= 0) {
                    continue;
                }
                WaitOutstockProductPojo waitPurchaseProductPojo = new WaitOutstockProductPojo();
                Double rate = purchaseProductEntity.getRate();
                if (Objects.nonNull(rate)){
                    shortageQuantity = Arith.div(shortageQuantity, rate);
                }
                waitPurchaseProductPojo.setProductNum(shortageQuantity);
                waitPurchaseProductPojoList.add(waitPurchaseProductPojo);
            }
            waitOutProductPojoMap.put(purchaseId, waitPurchaseProductPojoList);
        }
        for (PaasFormDataEntityExt purchase : formDataList) {
            JSONObject data = purchase.getData();
            Long purchaseId = purchase.getId();
            String purchaseName = FastJsonHelper.getStringOrDefault(data, WaitInstockEnum.PURCHASE_NAME.getAttr(), "");
            JSONArray supplierArray = data.getJSONArray(WaitInstockEnum.SUPPLIER_ID.getAttr());
            String supplierName = "";
            if (Objects.nonNull(supplierArray)){
                supplierName = FastJsonHelper.getStringOrDefault(supplierArray.getJSONObject(0), "name", "");
            }
            String signerName = FastJsonHelper.getStringOrDefault(data, WaitInstockEnum.SIGNER_ID.getAttr(), "");
            String serialNo = purchase.getSerialNo() == null ? "" : purchase.getSerialNo();
            JSONArray explains = new JSONArray();
            JSONObject others = new JSONObject();
            List<WaitOutstockProductPojo> waitPurchaseProductPojoList = waitOutProductPojoMap.getOrDefault(purchaseId, new ArrayList<>());
            Double num = 0D;
            for (WaitOutstockProductPojo waitPurchaseProductPojo : waitPurchaseProductPojoList) {
                num += waitPurchaseProductPojo.getProductNum();
            }
            others.put("waitStockProcduct", String.format(I18nMessageUtil.getMessage(StringConstant.PRODUCT_TYPE_NUM), waitPurchaseProductPojoList.size(), num));
            others.put("formId", purchase.getFormId());
            others.put("menuId", purchase.getMenuId());
            others.put(StringConstant.BUSINESS_TYPE, XbbRefTypeEnum.PURCHASE.getCode());
            others.put(StringConstant.DATA_ID, purchaseId);
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitInstockEnum.SHEET_NO.getAttrName(), serialNo));
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitInstockEnum.SUPPLIER_ID.getAttrName(), supplierName));
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitInstockEnum.SIGNER_ID.getAttrName(), signerName));
            appListPojos.add(new AppListPojo(purchaseId, purchaseName , explains,  others));
        }
        ListAppVO listAppVO = new ListAppVO();
        listAppVO.setList(appListPojos);
        listAppVO.setTotalCount(formDataListVO.getPageHelper().getRowsCount());
        return listAppVO;
    }

    /**
     * 获取移动端待入库中的产品维度信息
     *
     * @param formDataListDTO 列表查询DTO
     * @param formDataListVO  列表查询回参
     * @throws XbbException
     * @author jiping.yang
     * @date 2021-9-23 11:03
     */
    @Override
    public ListAppVO formatWaitProductInstock(FormDataListDTO formDataListDTO, PaasFormDataEsListVO formDataListVO) throws XbbException {
        List<AppListPojo> appListPojos = new ArrayList<>();
        List<PaasFormDataEntityExt> formDataList = formDataListVO.getPaasFormDataESList();
        for (PaasFormDataEntityExt purchase : formDataList) {
            JSONObject data = purchase.getData();
            Long purchaseId = purchase.getDataId();
            Long productId = data.getLong("productId");
            JSONArray productImg = data.getJSONArray(WaitInstockEnum.PRODUCT_IMGS.getAttr());
            JSONArray productNameArray = data.getJSONArray(WaitInstockEnum.PRODUCT_NAME.getAttr());
            String productName = "";
            if (Objects.nonNull(productNameArray)){
                productName = productNameArray.getJSONObject(0).getString("name");
            }
            String specification = FastJsonHelper.getStringOrDefault(data, WaitInstockEnum.SPECIFICATION.getAttr(), "");
            JSONArray productNOArray = data.getJSONArray(WaitInstockEnum.SHEET_NO.getAttr());
            String serialNo = "";
            if (Objects.nonNull(productNOArray)){
                serialNo = productNOArray.getJSONObject(0).getString("name");
            }

            JSONArray supplierArray = data.getJSONArray(WaitInstockEnum.SUPPLIER_ID.getAttr());
            String supplierName = "";
            if (Objects.nonNull(supplierArray)){
                supplierName = FastJsonHelper.getStringOrDefault(supplierArray.getJSONObject(0), "name", "");
            }
            Double num = data.getDouble(WaitInstockEnum.STOCK.getAttr()) == null ? 0 : data.getDouble(WaitInstockEnum.STOCK.getAttr());
            JSONArray explains = new JSONArray();
            JSONObject others = new JSONObject();
            others.put("formId", purchase.getFormId());
            others.put("menuId", purchase.getMenuId());
            others.put(StringConstant.BUSINESS_TYPE, XbbRefTypeEnum.PRODUCT.getCode());
            others.put(StringConstant.DATA_ID, purchaseId);
            others.put(StringConstant.PRODUCT_ID, productId);
            others.put("linkId", productId);
            others.put("productImage", productImg);
            others.put("num",num);
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitInstockEnum.SPECIFICATION.getAttrName(), specification));
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitInstockEnum.SHEET_NO.getAttrName(), serialNo));
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitInstockEnum.SUPPLIER_ID.getAttrName(), supplierName));
            explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, WaitInstockEnum.STOCK.getAttrName(), num));
            appListPojos.add(new AppListPojo(purchaseId, productName, explains, others));
        }
        ListAppVO listAppVO = new ListAppVO();
        listAppVO.setList(appListPojos);
        listAppVO.setTotalCount(formDataListVO.getPageHelper().getRowsCount());
        return listAppVO;
    }

    /**
     * 获取移动端待出库中的合同
     *
     * @param formDataListDTO 列表查询DTO
     * @param formDataListVO  列表查询回参
     * @throws XbbException
     * @author youli.chen
     * @date 2019-05-31 11:03
     */
    @Override
    public ListAppVO formatWaitContractOutstock(FormDataListDTO formDataListDTO, PaasFormDataEsListVO formDataListVO) throws XbbException {
        // 待出库可见权限
        UserVO loginUser = formDataListDTO.getLoginUser();
        if (!loginUser.getPermSet().contains(ProPermissionAliasEnum.PENDING_OUTSTOCK_VIEW.getAlias()) && !loginUser.isAdminOrBoss()){
            throw new XbbException(PurchaseErrorCodeEnum.API_ERROR_225038);
        }
        List<AppListPojo> appListPojos = new ArrayList<>();
        String corpid = formDataListDTO.getCorpid();
        List<PaasFormDataEntityExt> formDataList = formDataListVO.getPaasFormDataESList();
        if (formDataList.isEmpty()) {
            return new ListAppVO();
        }
        List<Long> dataIdList = new ArrayList<>();
        dataIdList.add(-1L);
        formDataList.forEach(item -> dataIdList.add(item.getId()));
        Set<Long> productIdSet = new HashSet<>();
        Map<String, Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        map.put("corpid", corpid);
        map.put("del", DelEnum.NORMAL.getDel());
        map.put("contractIdIn", dataIdList);
        map.put("status", 0);
        map.put("surplusNum",0);
        //获取合同产品信息
        List<ContractProductEntity> contractProductEntityList = contractProductModel.findEntitys(map);
        Map<Long, List<ContractProductEntity>> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //获取待出库数量
        Map<String, Double> productEntityMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("refIdIn", dataIdList);
        param.put("type", OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode());
        param.put("del", 0);
        param.put("columns","ref_id,product_id,ref_product_id,product_num");
        param.put(ParameterConstant.ORDER_BY_STR, BasicConstant.SORT);
        List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(param);
        for (OutstockProductEntity entity : outstockProductEntityList){
            String key;
            if (Objects.isNull(entity.getRefProductId())){
                key = entity.getRefId() + "_" + entity.getProductId();
            }else {
                key = entity.getRefId() + "_" + entity.getProductId() + "_" + entity.getRefProductId();
            }
            Double num = productEntityMap.getOrDefault(key,0D);
            num += entity.getProductNum();
            productEntityMap.put(key,num);
        }
        Iterator<ContractProductEntity> iterator = contractProductEntityList.iterator();
        while (iterator.hasNext()) {
            ContractProductEntity entity = iterator.next();
            String key = entity.getContractId() + "_" + entity.getProductId() + "_" + entity.getId();
            //先匹配新数据
            if (!productEntityMap.containsKey(key)) {
                //在匹配老数据
                key = entity.getContractId() + "_" + entity.getProductId();
                if (!productEntityMap.containsKey(key)) {
                    entity.setWaitOutStockNum(entity.getProductNum());
                }
            } else {
                Double outstockNum = productEntityMap.get(key);
                double productNum = entity.getProductNum() == null ? 0D : entity.getProductNum();
                double leftProductNum = Arith.sub(productNum, outstockNum);
                if (leftProductNum < 0.00000001) {
                    productEntityMap.put(key, 0D);
                } else {
                    productEntityMap.put(key, leftProductNum);
                }
                entity.setWaitOutStockNum(productEntityMap.getOrDefault(key, 0D));
            }
            List<ContractProductEntity> singleContractProductList = new ArrayList<>();
            singleContractProductList.add(entity);
            if (contractProductMap.containsKey(entity.getContractId())) {
                singleContractProductList.addAll(contractProductMap.get(entity.getContractId()));
            }
            contractProductMap.put(entity.getContractId(), singleContractProductList);
            productIdSet.add(entity.getProductId());
        }
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(), productIdSet));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_PRODUCT.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_PRODUCT.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 0, productIdSet.size());
        searchRequest.source(sourceBuilder);
        //关联产品的具体信息
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt entity : esEntities.getContent()) {
            productMap.put(entity.getDataId(), entity);
        }
        Map<Long, List<WaitOutstockProductPojo>> waitOutProductPojoMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (Map.Entry<Long, List<ContractProductEntity>> entry : contractProductMap.entrySet()) {
            List<ContractProductEntity> productEntityList = entry.getValue();
            Long contractId = entry.getKey();
            List<WaitOutstockProductPojo> waitOutProductPojoList = waitOutProductPojoMap.getOrDefault(contractId, new ArrayList<>());
            for (ContractProductEntity contractProductEntity : productEntityList) {
                Long productId = contractProductEntity.getProductId();
                if (!productMap.containsKey(productId)) {
                    continue;
                }
                Double shortageQuantity = contractProductEntity.getWaitOutStockNum();
                if (Objects.nonNull(contractProductEntity.getRate())){
                    shortageQuantity=Arith.div(shortageQuantity,contractProductEntity.getRate());
                }
                WaitOutstockProductPojo waitOutstockProductPojo = new WaitOutstockProductPojo();
                waitOutstockProductPojo.setProductNum(shortageQuantity);
                waitOutProductPojoList.add(waitOutstockProductPojo);
            }

            waitOutProductPojoMap.put(contractId, waitOutProductPojoList);
        }
        for (PaasFormDataEntityExt contract : formDataList) {
            JSONObject data = contract.getData();
            Long contractId = contract.getId();
            String contractName = FastJsonHelper.getStringOrDefaultFromFormData(data, ContractEnum.NAME.getAttr(), "");
            Long signTime = FastJsonHelper.getLongOrDefaultFromFormData(data, ContractEnum.SIGN_TIME.getAttr(), 0L);
            String signTimeStr = DateTimeUtil.getStringEpochSecond(signTime, DateTimeUtil.SDFDate);
            String signTimeShow = signTimeStr == null ? "--" : signTimeStr;
            JSONArray explains = new JSONArray();
            JSONObject others = new JSONObject();
            List<WaitOutstockProductPojo> waitOutstockProductPojoList = waitOutProductPojoMap.getOrDefault(contractId, new ArrayList<>());
            Double num = 0D;
            for (WaitOutstockProductPojo waitPurchaseProductPojo : waitOutstockProductPojoList) {
                num += waitPurchaseProductPojo.getProductNum();
            }
            others.put("waitStockProcduct", String.format(I18nMessageUtil.getMessage(StringConstant.PRODUCT_TYPE_NUM), waitOutstockProductPojoList.size(), num));
            others.put("formId", contract.getFormId());
            others.put("menuId", contract.getMenuId());
            others.put(StringConstant.BUSINESS_TYPE, XbbRefTypeEnum.CONTRACT.getCode());
            others.put(StringConstant.DATA_ID, contractId);
            others.put("sourceBusinessType", XbbRefTypeEnum.PRODUCT.getCode());
            if (Objects.equals(formDataListDTO.getDistributorMark(), DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, OrderEnum.ORDER_NO.getAttrName(), FastJsonHelper.getStringOrDefaultFromFormData(data, OrderEnum.ORDER_NO.getAttr(), "")));
                JSONArray linkDisyributor = data.getJSONArray(OrderEnum.LINK_DISTRIBUTOR.getAttr());
                if (linkDisyributor!=null && !linkDisyributor.isEmpty()){
                    JSONObject jsonObject= (JSONObject) linkDisyributor.get(0);
                    explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, OrderEnum.LINK_DISTRIBUTOR.getAttrName(), jsonObject.get("name")));
                }else {
                    explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, OrderEnum.LINK_DISTRIBUTOR.getAttrName(), ""));
                }
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, OrderEnum.OWNER_ID.getAttrName(), data.get(OrderEnum.OWNER_ID.getAttr())));
            } else {
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.CONTRACT_NO.getAttrName(), FastJsonHelper.getStringOrDefaultFromFormData(data, ContractEnum.CONTRACT_NO.getAttr(), "")));
                JSONArray linkCustomer = data.getJSONArray(ContractEnum.LINK_CUSTOMER.getAttr());
                if (linkCustomer!=null && !linkCustomer.isEmpty()){
                    JSONObject jsonObject= (JSONObject) linkCustomer.get(0);
                    explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.LINK_CUSTOMER.getAttrName(), jsonObject.get("name")));
                }else {
                    explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.LINK_CUSTOMER.getAttrName(), ""));
                }
                explains.add(String.format(StringConstant.APP_LIST_VALUE_SHOW, ContractEnum.OWNER_ID.getAttrName(),data.get(ContractEnum.OWNER_ID.getAttr())));
            }
            appListPojos.add(new AppListPojo(contract.getId(), contractName, explains, null, null, others));
        }
        ListAppVO listAppVO = new ListAppVO();
        listAppVO.setList(appListPojos);
        listAppVO.setTotalCount(formDataListVO.getPageHelper().getRowsCount());
        return listAppVO;
    }

    /**
     * 初始化现金毛利pojo
     *
     * @param updateCashProfitDTO
     * @return 现金毛利pojo
     */
    public CashProfitSavePojo initCashProfitPojo(ContractUpdateCashProfitDTO updateCashProfitDTO) throws XbbException {
        ContractEntityExt contractEntity = updateCashProfitDTO.getContractEntityExt();
        Boolean isAddPayment = updateCashProfitDTO.getIsAddPayment();
        PaasFormDataEntityExt paymentSheetEntityExt = updateCashProfitDTO.getPaymentSheetEntityExt();
        CashProfitSavePojo cashProfitSavePojo = new CashProfitSavePojo();
        cashProfitSavePojo.init(contractEntity);
        cashProfitSavePojo.setIsAddPayment(isAddPayment);
        Double oldContractCost = getDoubleOrDefaultFromFormData(contractEntity.getData(), ContractEnum.CONTRACT_COST.getAttr(), 0D);
        Double finishAmount = getDoubleOrDefaultFromFormData(paymentSheetEntityExt.getData(), PaymentSheetEnum.AMOUNT.getAttr(), 0D);
        cashProfitSavePojo.setOldContractCost(oldContractCost);
        cashProfitSavePojo.setFinishAmount(finishAmount);
        return cashProfitSavePojo;
    }

    /**
     * 现金毛利、毛利率
     *
     * @param cashProfitSavePojo 现金毛利保存信息
     * @return 现金毛利return
     * @author haibin.zhang
     */
    public CashProfitReturnVO getCashProfit(CashProfitSavePojo cashProfitSavePojo) throws XbbException {
        String corpid = cashProfitSavePojo.getCorpid();
        Long contractId = cashProfitSavePojo.getContractId();
        Double cashProfit = cashProfitSavePojo.getCashProfit();
        Double cashProfitRate = cashProfitSavePojo.getCashProfitRate();
        Double contractCost = cashProfitSavePojo.getContractCost();

        Double finishAmount = cashProfitSavePojo.getFinishAmount();
        Double oldContractCost = cashProfitSavePojo.getOldContractCost();

        //是否是老数据,需要刷数据;或新建合同
        Boolean oldDataInit = cashProfit == 0 || cashProfitRate == 0;
        //合同成本是否有修改
        Boolean contractCostHasChange = !Objects.equals(contractCost, oldContractCost);
        //已收回款是否有修改
        Boolean paymentHasChange = finishAmount != 0D;

        Boolean isAddPayment = cashProfitSavePojo.getIsAddPayment();

        Double newCashProfit = cashProfit;
        Double newCashProfitRate = cashProfitRate;
        if (oldDataInit) {
            String jsonData = StringConstant.JSON_DATA + StringConstant.POINT;
            List<AggSumDTO> sumList = new ArrayList<>();
            AggSumDTO aggSumDTO = new AggSumDTO();
            aggSumDTO.setAttr(jsonData + PaymentSheetEnum.AMOUNT.getAttr());
            NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
            boolQueryBuilder.filter(termsQuery(FieldTypeEnum.FLOW_STATUS.getAlias(), Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
            boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
            //TODO 资金管理改造，回款单中合同字段从单个变为多个，此处已错误，但是此方法没有用到暂时未处理
            boolQueryBuilder.filter(termQuery(jsonData + PaymentSheetEnum.CONTRACT.getAttr(), contractId));
            boolQueryBuilder.filter(termQuery(jsonData + PaymentSheetEnum.IS_BAD.getAttr(), 0));
            nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
            NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
            IndexTypeEnum indexTypeEnum = IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET;
            List<AggSumVO> aggSumList = esHelper.aggSumByField(indexTypeEnum.getIndex(), indexTypeEnum.getType(), sumList, searchQuery);
            Double finishAmountSum = 0D;
            for (AggSumVO aggSumVO : aggSumList) {
                if (PaymentSheetEnum.AMOUNT.getAttr().equals(aggSumVO.getAttr())) {
                    Double amount = aggSumVO.getValue() == null ? 0D : (Double) aggSumVO.getValue();
                    finishAmount += amount;
                }
            }
            newCashProfit = finishAmountSum - contractCost;
            if (!Objects.equals(0D, finishAmountSum)) {
                newCashProfitRate = newCashProfit / finishAmountSum;
            }
        } else if (paymentHasChange || contractCostHasChange) {
            Double oldFinishAmount = cashProfit + oldContractCost;
            if (paymentHasChange) {
                finishAmount = isAddPayment ? finishAmount : -finishAmount;
            }
            finishAmount += oldFinishAmount;
            newCashProfit = finishAmount - contractCost;
            if (finishAmount != 0D) {
                newCashProfitRate = newCashProfit / finishAmount;
            } else {
                newCashProfitRate = 0D;
            }
        }

        CashProfitReturnVO cashProfitReturnPojo = new CashProfitReturnVO();
        cashProfitReturnPojo.setCashProfit(newCashProfit);
        cashProfitReturnPojo.setCashProfitRate(newCashProfitRate);
        return cashProfitReturnPojo;
    }

    /**
     * 获取其他费用总和
     *
     * @param surchargeList 合同关联的其他费用
     * @author long.rao
     * @date 2019-03-05 17:30
     */
    public Double calculateSurcharge(List<ContractSurchargeEntity> surchargeList) {
        Double totalSurcharge = 0D;
        for (ContractSurchargeEntity surcharge : surchargeList) {
            if (Objects.equals(surcharge.getSurchargeType(), 2)) {
                //2018/6/22 凤娇优惠不计入成本
                continue;
            }
            Double surchargeAmount = surcharge.getAmount();
            totalSurcharge = Arith.add(totalSurcharge, surchargeAmount);
        }
        return totalSurcharge;
    }


    @Override
    public BaseVO changeStatus(ChangeStatusDTO changeStatusDTO) throws XbbException {
        // TODO 合同编辑权限校验
        Long dataId = changeStatusDTO.getDataId();
        Integer businessType = changeStatusDTO.getBusinessType();
        String corpid = changeStatusDTO.getCorpid();
        PaasFormDataEntityExt paasFormDataEntityExt = saasGetHelp.getFormData(dataId, corpid, businessType);
        if (paasFormDataEntityExt == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
        }
        JSONObject contractData = paasFormDataEntityExt.getData();
        /*SaasAnalysisDataDTO saasAnalysisDataDTO = new SaasAnalysisDataDTO();
        BeanUtil.copyProperties(changeStatusDTO, saasAnalysisDataDTO);
        // saas特殊格式转换
        saasUpdateHelp.formatData(saasAnalysisDataDTO, contractData);*/
        String type = changeStatusDTO.getType();
        String oldType = FastJsonHelper.getStringOrDefaultFromFormData(contractData, ContractEnum.STATUS.getAttr(), "");
        if (Objects.equals(oldType, type)) {
            throw new XbbException(ContractErrorCodeEnum.API_ERROR_203010);
        } else {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(ContractEnum.STATUS.getAttr(), type);
            UpdateDataEntity updateData = ExplainUtil.getUpdateData(dataId, jsonObject, corpid);
            contractModel.updateBatch(Collections.singletonList(updateData), corpid);
            /*contractData.put(ContractEnum.STATUS.getAttr(), type);
            FormDataUpdateDTO formDataUpdateDTO = new FormDataUpdateDTO();
            formDataUpdateDTO.setDataList(contractData);
            formDataUpdateDTO.setDataId(paasFormDataEntityExt.getId());
            formDataUpdateDTO.setCorpid(paasFormDataEntityExt.getCorpid());
            formDataUpdateDTO.setSaasMark(SaasMarkEnum.SAAS.getCode());
            formDataUpdateDTO.setBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
            BeanUtil.copyProperties(paasFormDataEntityExt, formDataUpdateDTO);
            paasFormDataService.update(formDataUpdateDTO);*/

            // 合同状态变更消息推送

            String typeName = dataDictionaryModel.getDictionaryValue(corpid, DictionaryEnum.CONTRACT_STATUS, type);
            List<String> pushUserList = pushHelper.getUserIdListByPushSetRule(corpid, PushTypeEnum.CONTRACT_CHANGE_PUSH.getCode(),
                    PushTypeEnum.CONTRACT_CHANGE_PUSH.getSubCode(), changeStatusDTO.getLoginUser(), paasFormDataEntityExt);
            PushRefTypePojo.Options options = new PushRefTypePojo.Options(paasFormDataEntityExt.getAppId(), paasFormDataEntityExt.getMenuId(),
                    paasFormDataEntityExt.getFormId(), dataId, Collections.singletonList(dataId), SaasMarkEnum.SAAS.getCode(),
                    changeStatusDTO.getBusinessType(), changeStatusDTO.getBusinessType());
            MessageRabbitMqDTO messageRabbitMqDTO = new MessageRabbitMqDTO(corpid, pushUserList, Collections.singletonList(dataId), 0,
                    null, I18nMessageUtil.getMessage(ContractPushConstant.CONTRACT_CHANGE_TITLE), String.format(I18nMessageUtil.getMessage(ContractPushConstant.CONTRACT_CHANGE_CONTENT),
                    changeStatusDTO.getLoginUserName(), paasFormDataEntityExt.getSerialNo(), typeName), null, options);
            baseProducer.sendMessage(PushTypeEnum.CONTRACT_CHANGE_PUSH, messageRabbitMqDTO);

            // 日志
            operationLogHelp.changeStatusLog(changeStatusDTO,paasFormDataEntityExt,updateData.getUpdateTime());
        }
        return new BaseVO();
    }

    /**
     * 合同产品校验
     *
     * @param productList
     * @return 出库完毕true
     * @throws XbbException
     */
    private boolean validateProduct(JSONArray productList, String corpid, Long contractId) throws XbbException {
        List<Long> pageProductIds = new ArrayList<>();
        pageProductIds.add(-1L);
        for (int i = 0; i < productList.size(); i++) {
            JSONObject productItemObj = productList.getJSONObject(i);
            Long productId = productItemObj.getLong(StringConstant.SAAS_LINK_BUSINESS_ID);
            // 产品数量
            Double productNum = productItemObj.getDouble(RelativeProductEnum.NUM.getSaasAttr());
            Double price = productItemObj.getDouble(RelativeProductEnum.SALE_PRODUCT_PRICE.getSaasAttr());
            price = price != null ? price : 0;
            // 折扣：默认为100%
            Double discount = productItemObj.getDouble(RelativeProductEnum.SALE_DISCOUNT.getSaasAttr());
            discount = discount == null ? 100D : discount;
            // 备注
            String memo = productItemObj.getString(RelativeProductEnum.MEMO.getSaasAttr());
            //去掉emoji表情
            memo = StringUtil.filterEmoji(memo);
            pageProductIds.add(productId);
            if (productNum == null) {
                throw new XbbException(ContractErrorCodeEnum.API_ERROR_203012);
            }
        }
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("productIdIn", pageProductIds);
        List<ProductEntityExt> products = productModel.findEntitys(param);
        /*******此段操作是为了判断产品是否被删除了，删除了的话是不能保存的**************************/
        StringBuilder productIsDel = new StringBuilder();
        for (ProductEntityExt product : products) {
            if (product.getDel() == 1) {
                String name = FastJsonHelper.getStringOrDefaultFromFormData(product.getData(), ProductEnum.NAME.getAttr(), "");
                productIsDel.append(name).append(",");
            }
        }
        if (productIsDel.length() > 0) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205018.getCode(), XbbRefTypeEnum.PRODUCT.getName() + productIsDel + ProductErrorCodeEnum.API_ERROR_205018.getMsg());
        }

        // TODO haibin.zhang 致亲爱的杰哥，你有个需求还没做，合同编辑时产品数量限制,不能少已出库数量
        boolean isOverOutstockOpen = companyConfigModel.hasBeenEnable(corpid, CompanyConfigEnum.CONTRACT_OUTSTOCK_OVER_SET.getAlias(), StringConstant.POSITIVE_NUMBER);
        boolean allbound = true;
        //获取已出库产品
        List<OutstockProductEntity> outstockProductList = getAlreadyOutStoreProduct(corpid, contractId);
        Map<Long, Double> productNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (Object obj : productList) {
            JSONObject productItemObj = (JSONObject) obj;
            Long productId = productItemObj.getLong(StringConstant.SAAS_LINK_BUSINESS_ID);
            Double productNum = productItemObj.getDouble(RelativeProductEnum.NUM.getSaasAttr());
            productNum = Arith.add(productNum, productNumMap.getOrDefault(productId, 0D));
            productNumMap.put(productId, productNum);
        }
//        Map<Long,Double> outSotckNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
//        for (OutstockProductEntity entity : outstockProductList){
//            outSotckNumMap.put(entity.getProductId(),entity.getProductNum());
//        }
        for (OutstockProductEntity entity : outstockProductList) {
            Long productId = entity.getProductId();
            Double outStockNum = entity.getProductNum();
            Double productNum = productNumMap.getOrDefault(productId, 0D);
            productNum = Arith.sub(productNum, outStockNum);
            productNumMap.put(productId, productNum);
        }
        for (Map.Entry<Long, Double> entry : productNumMap.entrySet()) {
            Double productNum = entry.getValue();
            if (!isOverOutstockOpen && productNum < 0) {
                throw new XbbException(ContractErrorCodeEnum.API_ERROR_203013);
            }
            if (productNum > 0) {
                allbound = false;
            }
        }

        return allbound;
    }



    /**
     * 获取合同已出库产品
     *
     * @param corpid
     * @param contractId
     * @throws XbbException
     * @author long.rao
     * @date 2019-04-25 10:22
     */
    private List<OutstockProductEntity> getAlreadyOutStoreProduct(String corpid, Long contractId) throws XbbException {
        Boolean isUseJxc = paasAppService.validateAppEnable(null, XbbRefTypeEnum.JXC.getAlias(), SaasMarkEnum.SAAS.getCode(), corpid);
        if (!isUseJxc) {
            // 未开启进销存
            return new ArrayList<>();
        }
        PaasFormDataEsListVO paasFormDataEsListVO = outstockService.getOutstockListFromEs(corpid, contractId, OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode(), 0);
        List<PaasFormDataEntityExt> paasFormDataESList = paasFormDataEsListVO.getPaasFormDataESList();
        List<Long> outstockIds = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt outstockEsEntity : paasFormDataESList) {
            outstockIds.add(outstockEsEntity.getDataId());
        }

        outstockIds.add(-1L);
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(StringConstant.CORPID, corpid);
        param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
        param.put("outstockIdIn", outstockIds);
        // 拿到所有的已出库产品
        List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(param);

        Collection<OutstockProductEntity> mergeOutstockProduct = outstockProductModel.mergeOutstockProduct(outstockProductEntityList);
        outstockProductEntityList.clear();
        outstockProductEntityList.addAll(mergeOutstockProduct);

        return outstockProductEntityList;
    }

    @Override
    public void formatExportValue(List<PaasFormDataEntityExt> paasFormDataESList, FormDataListDTO formDataListDTO, List<FieldAttrEntityForImport> fieldAttrList, Integer businessType) throws XbbException {
        FieldAttrEntityForImport paymentField = null;
        for (FieldAttrEntityForImport fieldAttrEntityForImport : fieldAttrList) {
            if (Objects.equals(fieldAttrEntityForImport.getAttr(), ContractEnum.ADD_PAYMENT.getAttr())) {
                paymentField = fieldAttrEntityForImport;
                break;
            }
        }
        Integer distributorMark = formDataListDTO.getDistributorMark();
        List<Long> businessIdIn = new ArrayList<>();
        paasFormDataESList.forEach(paasFormDataEntityExt -> businessIdIn.add(paasFormDataEntityExt.getId()));
        List<ContractProductEntity> contractProductByContractIdIn = contractProductModel.getContractProductByContractIdIn(formDataListDTO.getCorpid(), businessIdIn);
        List<PaasFormDataEntityExt> contractLinkPaymentList = new ArrayList<>();
        if (paymentField != null) {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(ParameterConstant.CORPID, formDataListDTO.getCorpid());
            param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            String keyword = StringConstant.POINT + StringConstant.KEY_WORD;
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            boolQueryBuilder.filter(termQuery(ParameterConstant.CORPID + keyword, formDataListDTO.getCorpid()));
            boolQueryBuilder.filter(termQuery(ParameterConstant.DEL, DelEnum.NORMAL.getDel()));
            String esData = StringConstant.JSON_DATA + StringConstant.POINT;
            boolQueryBuilder.filter(termsQuery(esData + PaymentEnum.CONTRACT.getAttr() + keyword, businessIdIn));
            contractLinkPaymentList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PAYMENT, boolQueryBuilder, PaasFormDataEntityExt.class, null);
        }
        Map<Long, List<ContractProductEntity>> contractProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, List<PaasFormDataEntityExt>> paymentMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> productIdIn = new ArrayList<>();
        contractProductByContractIdIn.forEach(contractProductEntity -> {
            productIdIn.add(contractProductEntity.getProductId());
            List<ContractProductEntity> productEntityList = new ArrayList<>();
            if (contractProductMap.containsKey(contractProductEntity.getContractId())) {
                productEntityList.addAll(contractProductMap.get(contractProductEntity.getContractId()));
            }
            productEntityList.add(contractProductEntity);
            contractProductMap.put(contractProductEntity.getContractId(), productEntityList);
        });
        List<ProductEntityExt> productList = productModel.getProductListByIdIn(formDataListDTO.getCorpid(), productIdIn, 0);
        Map<Long, ProductEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        productList.forEach(item -> productMap.put(item.getId(), item));
        contractLinkPaymentList.forEach(payment -> {
            List<PaasFormDataEntityExt> paymentEntityList = new ArrayList<>();
            Long linkContractId = payment.getData().getLong(PaymentEnum.CONTRACT.getAttr());
            if (paymentMap.containsKey(linkContractId)) {
                paymentEntityList.addAll(paymentMap.get(linkContractId));
            }
            paymentEntityList.add(payment);
            paymentMap.put(linkContractId, paymentEntityList);
        });
        PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByBusinessType4Distributor(XbbRefTypeEnum.PAYMENT.getCode(), formDataListDTO.getCorpid(), distributorMark);
        HandlerExplainDTO handlerExplainDTO = new HandlerExplainDTO();
        BeanUtil.copyProperties(formDataListDTO, handlerExplainDTO);

        Map<String, MultiUnitItemPoJo> groupBaseUnitStringMap = relativeProductHelp.getGroupBaseUnitStringMap(formDataListDTO.getCorpid());

        for (PaasFormDataEntityExt paasFormDataEntityExt : paasFormDataESList) {
            JSONObject data = paasFormDataEntityExt.getData();
            data.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), commonHelp.getPercentFormat(saasUpdateHelp.getDoubleIgnoreExceptionFormData(data, ContractEnum.CASH_PROFIT_RATE.getAttr(), 0D), 3, 2));
            data.put(ContractEnum.GROSS_PROFIT_RATE.getAttr(), commonHelp.getPercentFormat(saasUpdateHelp.getDoubleIgnoreExceptionFormData(data, ContractEnum.GROSS_PROFIT_RATE.getAttr(), 0D), 3, 2));
            data.put(ContractEnum.PAY_PERCENT.getAttr(), commonHelp.getPercentFormat(saasUpdateHelp.getDoubleIgnoreExceptionFormData(data, ContractEnum.PAY_PERCENT.getAttr(), 0D), 3, 2));
            data.put(ContractEnum.DISCOUNT.getAttr(), commonHelp.getPercentFormat(saasUpdateHelp.getDoubleIgnoreExceptionFormData(data, ContractEnum.DISCOUNT.getAttr(), 0D), 3, 2));
            List<ContractProductEntity> singleContractProductEntityList = contractProductMap.getOrDefault(paasFormDataEntityExt.getId(), new ArrayList<>());
            List<PaasFormDataEntityExt> singleContractPaymentList = paymentMap.getOrDefault(paasFormDataEntityExt.getId(), new ArrayList<>());
            JSONArray productArray = new JSONArray();
            for (ContractProductEntity entity : singleContractProductEntityList) {
                JSONObject productItemObj = entity.getData() == null ? new JSONObject() : entity.getData();
                if (Objects.nonNull(entity.getPriceData()) && entity.getPriceData().size() > BasicConstant.ZERO) {
                    entity.getData().putAll(entity.getPriceData());
                }
                double rate = entity.getRate() == null ? 1 : entity.getRate();
                // TODO 精度
                if (Objects.nonNull(entity.getPriceData()) && entity.getPriceData().size() > BasicConstant.ONE) {
                    productItemObj.put(ContractProductEnum.SELLING_PRICE.getAttr(), entity.getData().get(ContractProductEnum.SELLING_PRICE.getAttr()));
                    productItemObj.put(ContractProductEnum.PRICE.getAttr(), entity.getData().get(ContractProductEnum.PRICE.getAttr()));
                    productItemObj.put(ContractProductEnum.SUBTOTAL.getAttr(), Arith.mul(entity.getData().getDouble(SelectProductEnum.SELLING_PRICE.getAttr()), entity.getBusinessNum()));
                } else {
                    productItemObj.put(ContractProductEnum.SELLING_PRICE.getAttr(), entity.getPrice());
                    productItemObj.put(ContractProductEnum.PRICE.getAttr(), entity.getHistoricalPrice());
                    productItemObj.put(ContractProductEnum.SUBTOTAL.getAttr(), Arith.mul(entity.getPrice(), entity.getProductNum()));
                }

                productItemObj.put(ContractProductEnum.DISCOUNT.getAttr(), entity.getDiscount());


                productItemObj.put(SelectProductEnum.PRODUCT_NAME.getAttr(), entity.getProductName());
                productItemObj.put(SelectProductEnum.PRODUCT_NO.getAttr(), entity.getProductNo());
                // 25778 【成功部】合同订单导出产品规格显示错误
                if (productMap.containsKey(entity.getProductId())) {
                    ProductEntityExt productEntityExt = productMap.get(entity.getProductId());
                    String spec = productEntityExt.getData().getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr());
                    productItemObj.put(SelectProductEnum.SPECIFICATION.getAttr(), spec);
                    if (StringUtil.isNotEmpty(spec) && Objects.equals(spec, "{}")) {
                        productItemObj.put(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr(), "");
                    }
                    productItemObj.put(SelectProductEnum.PRODUCT_NO.getAttr(), productEntityExt.getSerialNo());
                    productItemObj.put(SelectProductEnum.UNIT.getAttr(), productEntityExt.getData().get(ProductEnum.UNIT.getAttr()));
                    productItemObj.put(ProductEnum.ENABLE_MULTI_UNIT.getAttr(), productEntityExt.getData().getIntValue(ProductEnum.ENABLE_MULTI_UNIT.getAttr()));
                } else {
                    productItemObj.put(SelectProductEnum.SPECIFICATION.getAttr(), entity.getSpecification());
                    productItemObj.put(SelectProductEnum.PRODUCT_NO.getAttr(), entity.getProductNo());
                }
                productItemObj.put(ContractProductEnum.NUM.getAttr(), entity.getProductNum());
                productItemObj.put(ContractProductEnum.MEMO.getAttr(), entity.getMemo());
                productItemObj.put(SelectProductEnum.BARCODE.getAttr(), entity.getBarcode());
                productItemObj.put(SelectProductEnum.OUTSTOCK_NUM.getAttr(), Arith.div(entity.getContractOutstockNum(), rate));
                Double num = Arith.div(Arith.sub(entity.getProductNum(), entity.getContractOutstockNum()), rate);
                Double waitOutstockNum = num >= 0 ? num : 0D;
                productItemObj.put(ContractProductEnum.WAIT_OUTSTOCK_NUM.getAttr(), waitOutstockNum);
                productItemObj.put(SelectProductEnum.RETURN_NUM.getAttr(), entity.getInstockNum());
                //基本单位时直接塞入productUnit
                if (Objects.isNull(entity.getBusinessUnit())) {
                    if (Objects.equals(productItemObj.getInteger(ProductEnum.ENABLE_MULTI_UNIT.getAttr()), BasicConstant.ONE)) {
                        productItemObj.put(ProductEnum.UNIT.getAttr(), groupBaseUnitStringMap.getOrDefault(productItemObj.getString(ProductEnum.UNIT.getAttr()), new MultiUnitItemPoJo()).getBaseValue());
                        entity.setProductUnit(productItemObj.getString(ProductEnum.UNIT.getAttr()));
                    }
                    productItemObj.put(SelectProductEnum.NUM.getAttr(), entity.getProductNum());
                } else {
                    productItemObj.put(ProductEnum.UNIT.getAttr(), entity.getBusinessUnit());
                    productItemObj.put(SelectProductEnum.NUM.getAttr(), entity.getBusinessNum());
                    productItemObj.put(SelectProductEnum.RETURN_NUM.getAttr(), Arith.div(entity.getInstockNum(), rate));
                }

                if (Objects.equals(productItemObj.getInteger(ProductEnum.ENABLE_MULTI_UNIT.getAttr()), BasicConstant.ONE)) {
                    productItemObj.put(SelectProductEnum.BASIC_UNIT.getAttr(), entity.getProductUnit());
                    productItemObj.put(SelectProductEnum.BASIC_UNIT_NUM.getAttr(), entity.getProductNum());
                } else {
                    productItemObj.put(SelectProductEnum.BASIC_UNIT.getAttr(), "");
                    productItemObj.put(SelectProductEnum.BASIC_UNIT_NUM.getAttr(), "");
                }
                productArray.add(productItemObj);
            }
            data.put(ContractEnum.PRODUCT.getAttr(), productArray);

            JSONArray paymentArray = new JSONArray();
            Map<String, Object> userMap = formDataListDTO.getUserMap();
            Map<String, Object> deptMap = formDataListDTO.getDeptMap();
            List<FieldAttrEntityForImport> fieldAttrEntityList = new ArrayList<>();
            if (paymentField != null) {
                fieldAttrEntityList = JSON.parseArray(JSON.toJSONString(paymentField.getSubForm().getItems()), FieldAttrEntityForImport.class);
            }
            List<FieldAttrEntity> originFieldAttrList = JSONArray.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
            for (PaasFormDataEntityExt paymentEntity : singleContractPaymentList) {
                JSONObject paymentItemObj = paymentEntity.getData();
                // 设置协同人和负责人
                userHelp.setOwnerAndCoUser(paymentEntity.getData(), paymentEntity, formDataListDTO.getSaasMark(), XbbRefTypeEnum.PAYMENT.getCode(), formDataListDTO.getUserAllMap());
                importHelper.formatCommonData4Export(userMap, deptMap, paymentEntity, paymentItemObj, originFieldAttrList, null, null, businessType, null);
                importHelper.formatDropDown4Export(fieldAttrEntityList, paymentItemObj, null);
                // 字段可见性判断
                for (FieldAttrEntity fieldAttrEntity : fieldAttrEntityList) {
                    if (ExplainUtil.isFieldNeedHide(fieldAttrEntity, handlerExplainDTO)) {
                        paymentItemObj.put(fieldAttrEntity.getAttr(), "");
                    }
                }
                paymentArray.add(paymentItemObj);
            }
            data.put(ContractEnum.ADD_PAYMENT.getAttr(), paymentArray);
            // 订单收货人
            if (Objects.equals(businessType, XbbRefTypeEnum.ORDER.getCode()) && Objects.equals(formDataListDTO.getDistributorMark(), DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                data.put(OrderEnum.LINK_PARTNER_RECEIVER.getAttr(), FastJsonHelper.getStringOrDefaultFromFormData(data, OrderEnum.LINK_PARTNER_RECEIVER_HIDE.getAttr(), ""));
            }
        }
    }

    /**
     * 更新现金毛利
     *
     * @param corpid             公司id
     * @param sheetAmount        回款单金额
     * @param contractId         合同id
     * @param linkContractEntity
     * @param distributorMark    经销商标识
     * @throws XbbException
     */
    @Override
    public void updateCashProfit(String corpid, Double sheetAmount, Long contractId, PaasFormDataEntity linkContractEntity, Integer distributorMark) throws XbbException {
        PaasFormDataEntity contract;
        if (Objects.nonNull(linkContractEntity)) {
            contract = linkContractEntity;
        } else {
            contract = contractModel.getByKey(contractId, corpid);
        }

        if (Objects.isNull(contract)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
        }
        Double finishMoney = getDoubleOrDefaultFromFormData(contract.getData(), ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
        Double oldCashProfit = finishMoney - getDoubleOrDefaultFromFormData(contract.getData(), ContractEnum.CONTRACT_COST.getAttr(), 0D);
        Double amount = getDoubleOrDefaultFromFormData(contract.getData(), ContractEnum.AMOUNT.getAttr(), 0D);
        Double badAmount = getDoubleOrDefaultFromFormData(contract.getData(), ContractEnum.BAD_AMOUNT.getAttr(), 0D);
        String orderStatus = FastJsonHelper.getStringFromFormData(contract.getData(), ContractEnum.STATUS.getAttr());
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(ContractEnum.CASH_PROFIT.getAttr(), oldCashProfit + sheetAmount);
        Double money = Arith.add(finishMoney, sheetAmount);
        if (Double.compare(money, BasicConstant.ZERO) == BasicConstant.ZERO) {
            jsonObject.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), BasicConstant.ZERO);
        } else {
            jsonObject.put(ContractEnum.CASH_PROFIT_RATE.getAttr(), Arith.round(Arith.div((oldCashProfit + sheetAmount), money), 4));
        }
        Double newFinishMoney = Arith.add(finishMoney, sheetAmount);
        Double newUnFinishMoney = Arith.sub(Arith.sub(amount, Arith.add(finishMoney, sheetAmount)), badAmount);
        jsonObject.put(ContractEnum.FINISH_AMOUNT.getAttr(), newFinishMoney);
        jsonObject.put(ContractEnum.UN_FINISH_AMOUNT.getAttr(), newUnFinishMoney);
        if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
            //订货单则还需要处理收款状态
            String orderPayMentStatus = OrderPayMentStatusEnum.getOrderPayMentStatus(amount, newUnFinishMoney);
            jsonObject.put(OrderEnum.RECEIVE_PAYMENT_STATUS.getAttr(), orderPayMentStatus);
            if (Objects.equals(orderStatus, OrderStatusEnum.PENDING_FINANCIAL_REVIEW.getCode())) {
                jsonObject.put(OrderEnum.STATUS.getAttr(), OrderStatusEnum.DELIVERED.getCode());
            }
        }
        if (Double.compare(amount, BasicConstant.ZERO) == BasicConstant.ZERO) {
            jsonObject.put(ContractEnum.PAY_PERCENT.getAttr(), BasicConstant.ZERO);
        } else {
            jsonObject.put(ContractEnum.PAY_PERCENT.getAttr(), Arith.round(Arith.div(newFinishMoney, amount), 4));
        }
        UpdateDataEntity updateData = ExplainUtil.getUpdateData(contractId, jsonObject, corpid);
        contractModel.updateBatch(Collections.singletonList(updateData), corpid);
        if (Objects.nonNull(linkContractEntity)) {
            linkContractEntity.getData().put(ContractEnum.FINISH_AMOUNT.getAttr(), newFinishMoney);
            linkContractEntity.getData().put(ContractEnum.UN_FINISH_AMOUNT.getAttr(), newUnFinishMoney);
        }
    }

    @Override
    public void updateBatchCashProfit(String corpid, Double sheetAmount, Collection<Long> contractIds) throws XbbException {
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("corpid", corpid);
        params.put("idIn", contractIds);
        List<ContractEntityExt> contracts = contractModel.findEntitys(params);
        if (Objects.isNull(contracts)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
        }
        List<UpdateDataEntity> updateList = new ArrayList<>();
        for (ContractEntityExt contract : contracts) {
            JSONObject updateData = new JSONObject();
            Double finishMoney = getDoubleOrDefaultFromFormData(contract.getData(), ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
            Double oldCashProfit = finishMoney - getDoubleOrDefaultFromFormData(contract.getData(), ContractEnum.CONTRACT_COST.getAttr(), 0D);
            Double amount = getDoubleOrDefaultFromFormData(contract.getData(), ContractEnum.AMOUNT.getAttr(), 0D);
            updateData.put(ContractEnum.CASH_PROFIT.getAttr(), oldCashProfit + sheetAmount);
            double money = Arith.add(finishMoney, sheetAmount);
            saasUpdateHelp.handleAttrPercent(updateData, oldCashProfit + sheetAmount, money, ContractEnum.CASH_PROFIT_RATE.getAttr());
            double newFinishMoney = Arith.add(finishMoney, sheetAmount);
            Double newUnFinishMoney = Arith.sub(amount, Arith.add(finishMoney, sheetAmount));
            updateData.put(ContractEnum.FINISH_AMOUNT.getAttr(), newFinishMoney);
            updateData.put(ContractEnum.UN_FINISH_AMOUNT.getAttr(), newUnFinishMoney);
            saasUpdateHelp.handleAttrPercent(updateData, newFinishMoney, amount, ContractEnum.PAY_PERCENT.getAttr());
            UpdateDataEntity updateDataEntity = ExplainUtil.getUpdateData(contract.getId(), updateData, corpid);
            updateList.add(updateDataEntity);
        }
        contractModel.updateBatch(updateList, corpid);
    }

    @Override
    public void dataConsistencyUpdateCustomer(String corpid, Long customerId, String customerName) throws XbbException {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0))
                    .filter(termQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.LINK_CUSTOMER), String.valueOf(customerId)));
            List<String> fieldList = new ArrayList<>();
            fieldList.add(FieldTypeEnum.DATAID.getAlias());
            List<PaasFormDataEntityExt> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT, filter, PaasFormDataEntityExt.class, fieldList);
            Set<Long> ids = new HashSet<>();
            for (PaasFormDataEntityExt esEntity : esEntities) {
                ids.add(esEntity.getDataId());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("idIn", ids);
            param.put("del", 0);
            List<ContractEntityExt> contractEntityExtList = contractModel.findEntitys(param);

            if (Objects.nonNull(contractEntityExtList) && !contractEntityExtList.isEmpty()) {
                List<ContractUpdateDTO> contractUpdateDTOList = new ArrayList<>();
                for (ContractEntityExt entityExt : contractEntityExtList) {
                    ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
                    contractUpdateDTO.setId(entityExt.getId());
                    JSONObject data = new JSONObject();
                    data.put(ContractEnum.LINK_CUSTOMER_HIDE.getAttr(), customerName);
                    contractUpdateDTO.setData(data);
                    contractUpdateDTOList.add(contractUpdateDTO);
                }
                ContractUpdateBatchDTO contractUpdateBatchDTO = new ContractUpdateBatchDTO();
                contractUpdateBatchDTO.setCorpid(corpid);
                contractUpdateBatchDTO.setContractUpdateList(contractUpdateDTOList);
                updateBatch(contractUpdateBatchDTO);
            }
            LOG.info("ContractServiceImpl.dataConsistencyUpdateCustomer ， corpid=" + corpid + " customerId=" + customerId + " customerName=" + customerName);
        } catch (Exception e) {
            LOG.error("ContractServiceImpl.dataConsistencyUpdateCustomer 出错， corpid=" + corpid + " customerId=" + customerId + " customerName=" + customerName, e);
        }
    }

    @Override
    public void dataConsistencyUpdatePartner(String corpid, Long customerId, String customerName) throws XbbException {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0))
                    .filter(termQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.LINK_PARTNER_CUSTOMER), String.valueOf(customerId)));
            List<String> fieldList = new ArrayList<>();
            fieldList.add(FieldTypeEnum.DATAID.getAlias());
            List<PaasFormDataEntityExt> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT, filter, PaasFormDataEntityExt.class, fieldList);
            Set<Long> ids = new HashSet<>();
            for (PaasFormDataEntityExt esEntity : esEntities) {
                ids.add(esEntity.getDataId());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("idIn", ids);
            param.put("del", 0);
            List<ContractEntityExt> contractEntityExtList = contractModel.findEntitys(param);

            if (Objects.nonNull(contractEntityExtList) && !contractEntityExtList.isEmpty()) {
                List<ContractUpdateDTO> contractUpdateDTOList = new ArrayList<>();
                for (ContractEntityExt entityExt : contractEntityExtList) {
                    ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
                    contractUpdateDTO.setId(entityExt.getId());
                    JSONObject data = new JSONObject();
                    data.put(ContractEnum.LINK_PARTNER_CUSTOMER_HIDE.getAttr(), customerName);
                    contractUpdateDTO.setData(data);
                    contractUpdateDTOList.add(contractUpdateDTO);
                }
                ContractUpdateBatchDTO contractUpdateBatchDTO = new ContractUpdateBatchDTO();
                contractUpdateBatchDTO.setCorpid(corpid);
                contractUpdateBatchDTO.setContractUpdateList(contractUpdateDTOList);
                updateBatch(contractUpdateBatchDTO);
            }
        } catch (Exception e) {
            LOG.error("ContractServiceImpl.dataConsistencyUpdatePartner 出错，corpid=" + corpid + "  customerId=" + customerId + " customerName=" + customerName, e);
        }
    }

    @Override
    public void dataConsistencyUpdateContact(String corpid, Long contactId, String contactName) throws XbbException {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0))
                    .filter(termQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.LINK_CONTACT), contactId));
            List<String> fieldList = new ArrayList<>();
            fieldList.add(FieldTypeEnum.DATAID.getAlias());
            List<PaasFormDataEntityExt> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT, filter, PaasFormDataEntityExt.class, fieldList);
            Set<Long> ids = new HashSet<>();
            for (PaasFormDataEntityExt esEntity : esEntities) {
                ids.add(esEntity.getDataId());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("idIn", ids);
            param.put("del", 0);
            List<ContractEntityExt> contractEntityExtList = contractModel.findEntitys(param);

            if (Objects.nonNull(contractEntityExtList) && !contractEntityExtList.isEmpty()) {
                List<ContractUpdateDTO> contractUpdateDTOList = new ArrayList<>();
                for (ContractEntityExt entityExt : contractEntityExtList) {
                    ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
                    contractUpdateDTO.setId(entityExt.getId());
                    JSONObject data = new JSONObject();
                    data.put(ContractEnum.LINK_CONTACT_HIDE.getAttr(), contactName);
                    contractUpdateDTO.setData(data);
                    contractUpdateDTOList.add(contractUpdateDTO);
                }
                ContractUpdateBatchDTO contractUpdateBatchDTO = new ContractUpdateBatchDTO();
                contractUpdateBatchDTO.setCorpid(corpid);
                contractUpdateBatchDTO.setContractUpdateList(contractUpdateDTOList);
                updateBatch(contractUpdateBatchDTO);
            }
        } catch (Exception e) {
            LOG.error("ContractServiceImpl.dataConsistencyUpdateContact 出错， corpid=" + corpid + "  contactId=" + contactId + "  contactName=" + contactName, e);
        }
    }

    @Override
    public void dataConsistencyUpdateQuotation(String corpid, Long quotationId, String quotationName) throws XbbException {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0))
                    .filter(termQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.QUOTATION_ID), quotationId));
            List<String> fieldList = new ArrayList<>();
            fieldList.add(FieldTypeEnum.DATAID.getAlias());
            List<PaasFormDataEntityExt> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_QUOTATION, filter, PaasFormDataEntityExt.class, fieldList);
            Set<Long> ids = new HashSet<>();
            for (PaasFormDataEntityExt esEntity : esEntities) {
                ids.add(esEntity.getDataId());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("idIn", ids);
            param.put("del", 0);
            List<QuotationEntityExt> quotationEntityExts = quotationModel.findEntitys(param);

            if (Objects.nonNull(quotationEntityExts) && !quotationEntityExts.isEmpty()) {
                List<ContractUpdateDTO> contractUpdateDTOList = new ArrayList<>();
                for (QuotationEntityExt entityExt : quotationEntityExts) {
                    ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
                    contractUpdateDTO.setId(entityExt.getId());
                    JSONObject data = new JSONObject();
                    data.put(ContractEnum.QUOTATION_NAME.getAttr(), quotationName);
                    contractUpdateDTO.setData(data);
                    contractUpdateDTOList.add(contractUpdateDTO);
                }
                ContractUpdateBatchDTO contractUpdateBatchDTO = new ContractUpdateBatchDTO();
                contractUpdateBatchDTO.setCorpid(corpid);
                contractUpdateBatchDTO.setContractUpdateList(contractUpdateDTOList);
                updateBatch(contractUpdateBatchDTO);
            }
        } catch (Exception e) {
            LOG.error("ContractServiceImpl.dataConsistencyUpdateQuotation 出错， corpid=" + corpid + "  quotationId=" + quotationId + "  quotationName=" + quotationName, e);
        }
    }

    @Override
    public boolean judgeContractAllOutBound(Long outstockId, Long contractId, String corpid, List<ProductSavePojo> productSavePojoList) throws XbbException {
        return contractHelp.judgeContractAllOutBound(outstockId, contractId, corpid, productSavePojoList);
    }

    /**
     * 更新时回滚
     *
     * @param idIn
     * @param corpid
     * @throws XbbException
     */
    @Override
    public void updateRollback(Collection<Long> idIn, String corpid) throws XbbException {
        if (Objects.isNull(idIn) || idIn.isEmpty()) {
            return;
        }
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("idIn", idIn);
        param.put(StringConstant.CORPID, corpid);
        List<ContractEntityExt> contracts = contractModel.findEntitysWithoutSub(param);
        updateRollback(contracts, corpid);
    }

    @Override
    public void updateRollback(List<ContractEntityExt> contracts, String corpid) throws XbbException {
        if (CollectionUtils.isNotEmpty(contracts)) {
            for (ContractEntityExt item : contracts) {
                PaasFormDataEntity paasFormDataEntity = new PaasFormDataEntity();
                BeanUtil.copyProperties(item, paasFormDataEntity);
                proEsHelper.reductionEsInUpdate(paasFormDataEntity, item.getId(), corpid, IndexTypeEnum.IDX_SAAS_CONTRACT);
            }
        }
    }

    @Override
    public Double getCost(PaasFormDataEntityExt contract) throws XbbException {
        Long contractId = contract.getId();
        String corpid = contract.getCorpid();
        Double cost = 0D;
        JSONObject data = contract.getData();
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("contractId", contractId);
        param.put("del", 0);
        List<ContractProductEntity> contractProductList = contractProductModel.findEntitys(param);

        // 算出合同中每种产品的平均价
        Map<Long, String> avgCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (ContractProductEntity contractProductEntity : contractProductList) {
            Long productId = contractProductEntity.getProductId();
            Double proNum = contractProductEntity.getProductNum() == null ? 0D : Math.abs(contractProductEntity.getProductNum());
            if (avgCostMap.containsKey(productId)) {
                // map中已经有相同产品，需要计算平均值
                String productInfo = avgCostMap.get(productId);
                String[] split = productInfo.split(",");
                Double preCost = StringUtil.toDouble((split[0]), 0D);
                Double preNum = StringUtil.toDouble((split[1]), 0D);
                Double nowCost = SaasProductHelp.calculateCost(preNum, preCost, proNum, contractProductEntity.getCost(), 1);
                Double nowNum = Arith.add(preNum, proNum);
                avgCostMap.put(productId, nowCost + "," + nowNum);
            } else {
                // 将产品id为key，合同产品平均价和数量拼接字符串为value存入map中
                String productInfo = contractProductEntity.getCost() + "," + proNum;
                avgCostMap.put(productId, productInfo);
            }
        }

        // 获取合同中未出库产品数量
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        // 出库类型：1采购退货出库，2销售出库，3盘亏出库，4调拨出库，5其他出库，6装配出库, 7工单出库
        boolQueryBuilder.filter(termQuery("data." + OutstockEnum.TYPE.getAttr(), OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
        boolQueryBuilder.filter(termQuery("data." + OutstockEnum.REF_ID.getAttr(), contractId));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        List<String> fieldList = new ArrayList<>();
        fieldList.add(FieldTypeEnum.DATAID.getAlias());
        List<PaasFormDataEntityExt> outstockList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_OUTSTOCK, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        List<Long> outstockIdIn = new ArrayList<>();
        for (PaasFormDataEntityExt outstockEsEntity : outstockList) {
            outstockIdIn.add(outstockEsEntity.getDataId());
        }

        // 拿到所有的已出库产品
        List<OutstockProductEntity> outstockProductEntityList = new ArrayList<>();
        if (!outstockIdIn.isEmpty()) {
            param.clear();
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("outstockIdIn", outstockIdIn);
            outstockProductEntityList = outstockProductModel.findEntitys(param);
        }

        // 合同实际成本
        Double realCost = 0D;
        // 计算合同出库成本：SUM（已出库产品数量*出库产品成本）
        for (OutstockProductEntity outstockProductEntity : outstockProductEntityList) {
            Double productNum = outstockProductEntity.getProductNum();
            productNum = productNum == null ? 0D : productNum;
            Double outstockCost = outstockProductEntity.getCost();
            outstockCost = outstockCost == null ? 0D : outstockCost;
            realCost = Arith.add(realCost, Arith.mul(productNum, outstockCost));
        }

        Collection<OutstockProductEntity> mergeOutstockProduct = outstockProductModel.mergeOutstockProduct(outstockProductEntityList);
        outstockProductEntityList.clear();
        outstockProductEntityList.addAll(mergeOutstockProduct);

        Collection<ContractProductEntity> mergeProducts = mergeContractProduct(contractProductList);
        contractProductList.clear();
        contractProductList.addAll(mergeProducts);

        Iterator<ContractProductEntity> it = contractProductList.iterator();
        while (it.hasNext()) {
            ContractProductEntity contractProductEntity = it.next();
            for (OutstockProductEntity outstockProductEntity : outstockProductEntityList) {
                if (contractProductEntity.getProductId().equals(outstockProductEntity.getProductId())) {
                    Double proNum = contractProductEntity.getProductNum() == null ? 0D : Math.abs(contractProductEntity.getProductNum());
                    double num = Arith.sub(proNum, outstockProductEntity.getProductNum());
                    if (num <= 0.00000001) {
                        //如果合同产品数量少于已出库数量，那么就将该产品去掉不显示.（出现这种情况是因为合同重新编辑数量变少了）
                        it.remove();
                        break;
                    } else {
                        contractProductEntity.setProductNum(num);
                    }
                }
            }
        }
        // 计算合同预估成本
        Double estimateCost = 0D;
        for (ContractProductEntity contractProductEntity : contractProductList) {
            String productInfo = avgCostMap.get(contractProductEntity.getProductId());
            if (StringUtils.isEmpty(productInfo)) {
                continue;
            }
            String[] productInfos = productInfo.split(",");
            estimateCost = Arith.add(estimateCost, Arith.mul(contractProductEntity.getProductNum(), StringUtil.toDouble((productInfos[0]), 0D)));
        }

        Double surchargeAmount = getDoubleOrDefaultFromFormData(data, ContractEnum.OTHER_EXPENSE.getAttr(), 0D);
        cost = Arith.add(Arith.add(estimateCost, realCost), surchargeAmount);
        cost = Arith.round(cost, 2);
        return cost;
    }


    @Override
    public ProductSummaryVO summaryProductList(BusinessProductListDTO businessProductListDTO) throws XbbException {
        // 获取参数
        String corpid = businessProductListDTO.getCorpid();
        Long dataId = businessProductListDTO.getDataId();
        String attr = businessProductListDTO.getAttr();
        PaasFormDataEntityExt contract = contractModel.getByKey(dataId, corpid);
        if (Objects.isNull(contract)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
        }
        Map<String, Object> modelMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        modelMap.put(ParameterConstant.CORPID, corpid);
        modelMap.put(ParameterConstant.CONTRACT_ID, dataId);
        modelMap.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        modelMap.put("columns", "product_num, price,business_num,data");
        List<ContractProductEntity> contractProductEntities = contractProductModel.findEntitys(modelMap);

        ProductSummaryVO businessProductListVO = new ProductSummaryVO();
        PaasFormExplainEntity contractExplain = paasFormExplainModel.getByFormId(contract.getFormId(), businessProductListDTO.getCorpid());
        if (Objects.isNull(contractExplain)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(contractExplain.getExplains());
        // 关联产品字段
        FieldAttrEntity productField = explainMap.get(attr);
        // 产品合计
        FieldAttrEntity productTotalField = explainMap.get(ContractEnum.PRODUCT_TOTAL.getAttr());
        Double num = 0D;
        Double count = 0D;
        for (ContractProductEntity productEntity : contractProductEntities) {
            Double productNum = productEntity.getProductNum() != null ? productEntity.getProductNum() : 0D;
            Double price = productEntity.getPrice() == null ? 0D : productEntity.getPrice();
            double priceTotal = Objects.nonNull(productEntity.getData()) && Objects.nonNull(productEntity.getData().getDouble(ContractProductEnum.SUBTOTAL.getAttr())) ? productEntity.getData().getDouble(ContractProductEnum.SUBTOTAL.getAttr()) : Arith.mul(productNum, price);
            count = Arith.add(count, BigDecimal.valueOf(priceTotal).setScale(productTotalField.getAccuracy(), BigDecimal.ROUND_HALF_UP).doubleValue());
            num = Arith.add(num, productEntity.getBusinessNum() == null ? productNum : productEntity.getBusinessNum());
        }
        Boolean hidePrice = false;
        Integer productSize = contractProductEntities.size();
        String summaryInfo = String.format(I18nMessageUtil.getMessage(StringConstant.PRODUCTS_TYPE_NUM), productSize, Arith.doubleToBigDecimal(num));
        businessProductListVO.setSummaryInfo(summaryInfo);

        if (Objects.nonNull(productField)) {
            Map<Integer, List<UserTeamEntity>> userTeamMap = userTeamService.getUserTeam(dataId, corpid, XbbRefTypeEnum.CONTRACT.getCode());
            List<String> ownerIds = new ArrayList<>();
            List<String> coUserIds = new ArrayList<>();
            for (Map.Entry<Integer, List<UserTeamEntity>> entry : userTeamMap.entrySet()) {
                if (Objects.equals(entry.getKey(), BasicConstant.MAIN_USER)) {
                    entry.getValue().forEach(team -> ownerIds.add(team.getUserId()));
                } else if (Objects.equals(entry.getKey(), BasicConstant.COOP_USER)) {
                    entry.getValue().forEach(team -> coUserIds.add(team.getUserId()));
                }
            }
            HandlerExplainDTO handlerExplainDTO = new HandlerExplainDTO();
            BeanUtil.copyProperties(businessProductListDTO, handlerExplainDTO);
            handlerExplainDTO.setOwnerId(ownerIds);
            handlerExplainDTO.setCoUserId(coUserIds);
            handlerExplainDTO.setCreatorId(contract.getCreatorId());
            SubFormPoJo subForm = productField.getSubForm();
            List<? extends FieldAttrEntity> fieldAttrEntityList = subForm.getItems();
            for (FieldAttrEntity fieldAttrEntity : fieldAttrEntityList) {
                if (Objects.equals(fieldAttrEntity.getAttr(), ContractProductEnum.SELLING_PRICE.getAttr())) {
                    if (ExplainUtil.isFieldNeedHide(fieldAttrEntity, handlerExplainDTO)) {
                        hidePrice = true;
                    }
                }
            }
        }

        businessProductListVO.setMoneyInfo(String.format(I18nMessageUtil.getMessage(StringConstant.PRODUCTS_MONEY_COUNT), I18nMessageUtil.getMessage(I18nStringConstant.SELL_PRICE), AttrDecimalPrecisionHelper.parseThousandth(count, productTotalField.getAccuracy())));
        if (hidePrice) {
            businessProductListVO.setMoneyInfo(String.format(I18nMessageUtil.getMessage(StringConstant.PRODUCTS_MONEY_COUNT), I18nMessageUtil.getMessage(I18nStringConstant.SELL_PRICE), StringConstant.INVISIBLE_VALUE));
        }
        JSONObject others = new JSONObject();
        others.put("hidePrice", hidePrice);
        others.put("amount", count);
        businessProductListVO.setOthers(others);
        Boolean isJxcUse = paasAppService.validateAppEnable(null, XbbRefTypeEnum.JXC.getAlias(), SaasMarkEnum.SAAS.getCode(), corpid);
        if (isJxcUse) {
            //添加关闭和开启合同的按钮
            JSONObject data = contract.getData();
            String shipStatus = FastJsonHelper.getStringOrDefaultFromFormData(data, ContractEnum.SHIP_STATUS.getAttr(), ShipStatusEnum.UNSHIPPED.getCode());
            if (!Objects.equals(shipStatus, ShipStatusEnum.SHIPPED.getCode()) && num != 0D) {
                ButtonPojo buttonPojo = new ButtonPojo();
                if (Objects.equals(shipStatus, ShipStatusEnum.CLOSED.getCode()) && businessProductListDTO.getLoginUser().getPermSet().contains(EnableButtonEnum.CONTRACT_ENABLE.getPermission())) {
                    buttonPojo.setAttr("contract");
                    buttonPojo.setValue(OperateTypeEnum.ENABLE.getName());
                    buttonPojo.setLinkBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
                    List<ButtonPojo> topRightButton = new ArrayList<>();
                    topRightButton.add(buttonPojo);
                    businessProductListVO.setTopRightButton(topRightButton);
                } else if (!Objects.equals(shipStatus, ShipStatusEnum.CLOSED.getCode()) && businessProductListDTO.getLoginUser().getPermSet().contains(EnableButtonEnum.CONTRACT_UNABLE.getPermission())) {
                    buttonPojo.setAttr("contract");
                    buttonPojo.setValue(OperateTypeEnum.UNABLE.getName());
                    buttonPojo.setLinkBusinessType(XbbRefTypeEnum.CONTRACT.getCode());
                    List<ButtonPojo> topRightButton = new ArrayList<>();
                    topRightButton.add(buttonPojo);
                    businessProductListVO.setTopRightButton(topRightButton);
                }
            }
        }
        return businessProductListVO;
    }

    @Override
    public List<UpdateDataEntity> handleContract(Integer distributorMark, PaymentSheetTypeEnum paymentSheetTypeEnum, String corpid, Map<Long, Double> contractIdToAmount, Set<Long> contractIds) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("idIn", contractIds);
        List<ContractEntityExt> contracts = contractModel.findEntitys(param);
        if (Objects.isNull(contracts)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
        }
        boolean isRedFlag = PaymentSheetTypeEnum.isRed(paymentSheetTypeEnum.getCode());
        boolean isBadFlag = PaymentSheetTypeEnum.isBad(paymentSheetTypeEnum.getCode());
        List<UpdateDataEntity> updateList = new ArrayList<>();
        for (ContractEntityExt contractEntityExt : contracts) {
            String name = contractEntityExt.getSerialNo();
            if (Objects.equals(contractEntityExt.getDel(), DelEnum.DELETE.getDel())) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208032, PaymentErrorCodeEnum.API_ERROR_208032.getMsg(), I18nMessageUtil.getMessage(CommonConstant.CONTRACT), name);
            }
            Long contractId = contractEntityExt.getId();
            Double value = contractIdToAmount.getOrDefault(contractId, 0D);
            if (value == 0) {
                continue;
            }
            Double tempAmount = value;
            if (isRedFlag) {
                //红冲的，先把红冲金额取正数，然后判断红冲金额是否小于已收
                tempAmount = Math.abs(value);
            }
            JSONObject data = contractEntityExt.getData();
            Double finishMoney = getDoubleOrDefaultFromFormData(data, ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
            Double unAmount = getDoubleOrDefaultFromFormData(data, ContractEnum.UN_FINISH_AMOUNT.getAttr(), 0D);
            //校验填充的金额是否超过合同的已收/未收（根据是否红冲判断不同）
            if (isRedFlag) {
                //红冲，限制红冲金额tempAmount小于等于合同已收金额finishMoney
                if (Arith.sub(tempAmount, finishMoney) > ConfigConstant.amountAccuracy) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_RED, I18nMessageUtil.getMessage(CommonConstant.CONTRACT) + name);
                }
                //红冲的，把金额置为负数。用tempAmount计算金额是否合法
                value = Arith.mul(value, -1);
            } else if (Arith.sub(value, unAmount) > ConfigConstant.amountAccuracy) {
                //普通回款、坏账，限制回款金额小于等于合同未收金额
                String msg = I18nMessageUtil.getMessage(ErrorMessageConstant.DETAIL_BEYOND_AMOUNT);
                if (isBadFlag) {
                    msg = I18nMessageUtil.getMessage(ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_BAD);
                }
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, msg, I18nMessageUtil.getMessage(CommonConstant.CONTRACT), name);
            }
            JSONObject updateData = new JSONObject();
            /*
             处理合同毛利，及新的已收未收
             */
            //旧的现金毛利 = 已收金额 - 合同成本
            Double oldCashProfit = finishMoney - getDoubleOrDefaultFromFormData(data, ContractEnum.CONTRACT_COST.getAttr(), 0D);
            Double newCashProfit;
            Double newFinishMoney;
            Double badAmount = getDoubleOrDefaultFromFormData(data, ContractEnum.BAD_AMOUNT.getAttr(), 0D);
            Double amount = getDoubleOrDefaultFromFormData(data, ContractEnum.AMOUNT.getAttr(), 0D);
            Double newUnFinishMoney;
            if (isBadFlag) {
                /*
                 坏账时：已收不变，合同总额不变，未收减少，坏账增加，收款比例不变（= 已收/总合同额）
                 */
                newFinishMoney = getDoubleOrDefaultFromFormData(data, ContractEnum.FINISH_AMOUNT.getAttr(), 0D);
                Double oldUnFinishMoney = getDoubleOrDefaultFromFormData(data, ContractEnum.UN_FINISH_AMOUNT.getAttr(), 0D);
                newUnFinishMoney = Arith.sub(oldUnFinishMoney, value);
                //坏账增加
                Double newBadAmount = Arith.add(badAmount, value);
                //坏账，毛利不变
                newCashProfit = oldCashProfit;
                updateData.put(ContractEnum.BAD_AMOUNT.getAttr(), newBadAmount);
            } else {
                /*
                 普通、红冲回款时（红冲是负值，所以可以和普通回款一样处理方式）：已收增加，合同总额不变，未收减少，坏账不变-收款比例增加（= 已收/总合同额），毛利增加
                 */
                //此处直接add没问题：红冲时value是负数
                //注：为防止与原逻辑不一致，此处来回加减的代码参照旧代码内对各种金额的处理
                newFinishMoney = Arith.add(finishMoney, value);
                //合同未收 = 合同金额 - 合同已收 - 合同坏账
                newUnFinishMoney = Arith.sub(amount, newFinishMoney);
                newUnFinishMoney = Arith.sub(newUnFinishMoney, badAmount);
                updateData.put(ContractEnum.FINISH_AMOUNT.getAttr(), newFinishMoney);
                saasUpdateHelp.handleAttrPercent(updateData, newFinishMoney, amount, ContractEnum.PAY_PERCENT.getAttr());
                newCashProfit = oldCashProfit + value;
            }
            String orderStatus = FastJsonHelper.getStringFromFormData(contractEntityExt.getData(), ContractEnum.STATUS.getAttr());
            if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                //订货单则还需要处理收款状态
                String orderPayMentStatus = OrderPayMentStatusEnum.getOrderPayMentStatus(amount, newUnFinishMoney);
                updateData.put(OrderEnum.RECEIVE_PAYMENT_STATUS.getAttr(), orderPayMentStatus);
                if (Objects.equals(orderStatus, OrderStatusEnum.PENDING_FINANCIAL_REVIEW.getCode())) {
                    updateData.put(OrderEnum.STATUS.getAttr(), OrderStatusEnum.DELIVERED.getCode());
                }

            }
            updateData.put(ContractEnum.UN_FINISH_AMOUNT.getAttr(), newUnFinishMoney);
            updateData.put(ContractEnum.CASH_PROFIT.getAttr(), newCashProfit);
            saasUpdateHelp.handleAttrPercent(updateData, newCashProfit, newFinishMoney, ContractEnum.CASH_PROFIT_RATE.getAttr());
            UpdateDataEntity updateDataEntity = ExplainUtil.getUpdateData(contractId, updateData, corpid);
            updateList.add(updateDataEntity);
        }
        return updateList;
    }

    @Override
    public List<UpdateDataEntity> updateContract4Del(String corpid, Set<String> errorTypeSet, Set<String> errorDataSet, Collection<Long> delIdList, SheetDeleteHandleRelateDTO sheetDeleteHandleRelateDTO) throws XbbException {
        Map<Long, Double> contractIdToAmount = sheetDeleteHandleRelateDTO.getContractIdToAmount();
        Map<Long, Double> contractIdToUnAmount = sheetDeleteHandleRelateDTO.getContractIdToUnAmount();
        Map<Long, Double> contractIdToBad = sheetDeleteHandleRelateDTO.getContractIdToBad();
        Set<Long> contractIds = sheetDeleteHandleRelateDTO.getContractIds();
        Map<Long, List<Long>> cidSheetIdListMap = sheetDeleteHandleRelateDTO.getCidSheetIdListMap();
        Map<Long, List<String>> cidSheetNoListMap = sheetDeleteHandleRelateDTO.getCidSheetNoListMap();
        List<String> delNoList = sheetDeleteHandleRelateDTO.getDelNoList();
        ContractEnum enumAmount = ContractEnum.AMOUNT;
        ContractEnum enumCost = ContractEnum.CONTRACT_COST;
        ContractEnum enumFinishAmount = ContractEnum.FINISH_AMOUNT;
        ContractEnum enumUnFinishAmount = ContractEnum.UN_FINISH_AMOUNT;
        ContractEnum enumBadAmount = ContractEnum.BAD_AMOUNT;
        ContractEnum enumPayPercent = ContractEnum.PAY_PERCENT;
        ContractEnum enumCashProfit = ContractEnum.CASH_PROFIT;
        ContractEnum enumCashProfitRate = ContractEnum.CASH_PROFIT_RATE;
        List<String> fieldList = Arrays.asList(BasicConstant.ID, "del", ContractEnum.CONTRACT_NO.getAttr(), ContractEnum.getAttrConnectData(enumAmount), ContractEnum.getAttrConnectData(enumCost), ContractEnum.getAttrConnectData(enumFinishAmount), ContractEnum.getAttrConnectData(enumUnFinishAmount), ContractEnum.getAttrConnectData(enumBadAmount), ContractEnum.getAttrConnectData(enumPayPercent), ContractEnum.getAttrConnectData(enumCashProfit), ContractEnum.getAttrConnectData(enumCashProfitRate));
        List<PaasFormDataEntityExt> contracts = formHelp.getFormListByIds4All(corpid, IndexTypeEnum.IDX_SAAS_CONTRACT, contractIds, fieldList);
        List<UpdateDataEntity> updateList = new ArrayList<>();
        if (Objects.isNull(contracts)) {
            //查不到关联数据，则不关联更新
            return updateList;
        }
        for (PaasFormDataEntityExt entityExt : contracts) {
            if (Objects.equals(entityExt.getDel(), DelEnum.DELETE.getDel())) {
                //合同已经删除，则跳过不处理
                continue;
            }
            Long contractId = entityExt.getId();
            JSONObject updateData = new JSONObject();
            JSONObject data = entityExt.getData();
            double finishAmount = commonHelp.handleAttrAmount(enumFinishAmount.getAttr(), contractIdToAmount, contractId, data, updateData);
            //已收小于0，则不能更新该合同、且不能删除该合同对应的回款单集合
            if (finishAmount < BasicConstant.ZERO_DOUBLE) {
                //封装不能删除信息，并从delIdList、delNoList移除
                commonHelp.removeInfoAndAmountBack4DelError(true, I18nMessageUtil.getMessage(ErrorMessageConstant.NON_ZERO_CONTRACT_PAYMENT_AMOUNT), errorTypeSet, errorDataSet, cidSheetNoListMap, cidSheetIdListMap, delIdList, delNoList, contractId, sheetDeleteHandleRelateDTO);
                continue;
            }
            Double unAmount = commonHelp.handleAttrAmount(enumUnFinishAmount.getAttr(), contractIdToUnAmount, contractId, data, updateData);
            //未收小于0，则不能更新该合同、且不能删除该合同对应的回款单集合
            if (unAmount < BasicConstant.ZERO_DOUBLE) {
                //封装不能删除信息，并从delIdList、delNoList移除
                commonHelp.removeInfoAndAmountBack4DelError(true, I18nMessageUtil.getMessage(ErrorMessageConstant.NON_ZERO_CONTRACT_PAYMENT_AMOUNT_UN), errorTypeSet, errorDataSet, cidSheetNoListMap, cidSheetIdListMap, delIdList, delNoList, contractId, sheetDeleteHandleRelateDTO);
                continue;
            }
            Double badAmount = commonHelp.handleAttrAmount(enumBadAmount.getAttr(), contractIdToBad, contractId, data, updateData);
            //坏账小于0，则不能更新该合同、且不能删除该合同对应的回款单集合
            if (badAmount < BasicConstant.ZERO_DOUBLE) {
                //封装不能删除信息，并从delIdList、delNoList移除
                commonHelp.removeInfoAndAmountBack4DelError(true, I18nMessageUtil.getMessage(ErrorMessageConstant.NON_ZERO_CONTRACT_PAYMENT_AMOUNT_BAD), errorTypeSet, errorDataSet, cidSheetNoListMap, cidSheetIdListMap, delIdList, delNoList, contractId, sheetDeleteHandleRelateDTO);
                continue;
            }
            if (updateData.size() == 0) {
                //已收、未收、坏账金额都没变动
                continue;
            }
            Double amount = getDoubleOrDefaultFromFormData(data, enumAmount.getAttr(), 0D);
            saasUpdateHelp.handleAttrPercent(updateData, finishAmount, amount, enumPayPercent.getAttr());
            //现金毛利 = 已收金额 - 合同成本
            Double newCashProfit = finishAmount - getDoubleOrDefaultFromFormData(data, enumCost.getAttr(), 0D);
            updateData.put(enumCashProfit.getAttr(), newCashProfit);
            saasUpdateHelp.handleAttrPercent(updateData, newCashProfit, finishAmount, enumCashProfitRate.getAttr());
            UpdateDataEntity updateDataEntity = ExplainUtil.getUpdateData(contractId, updateData, corpid);
            updateList.add(updateDataEntity);
        }
        return updateList;
    }

    @Override
    public List<ContractEntityExt> getContractListByIds(String corpid, List<Long> contractIds) throws XbbException {
        if (contractIds == null || contractIds.size() == 0) {
            return new ArrayList<>();
        }
        Map<String, Object> params = new HashMap<>(4);
        params.put("corpid", corpid);
        params.put("idIn", contractIds);
        params.put("start", 0);
        params.put("pageNum", contractIds.size());
        return contractModel.findEntitys(params);
    }

    @Override
    public Map<Long, String> getContractNameMap(List<Long> handoverIdSet, String corpid) throws XbbException {
        Map<Long, String> contractNameMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (handoverIdSet.isEmpty()) {
            return contractNameMap;
        }
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(), handoverIdSet));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_CONTRACT.getType()));
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(boolQueryBuilder);
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 1, handoverIdSet.size());
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_CONTRACT.getIndex());
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<PaasFormDataEntity> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntity.class);
        for (PaasFormDataEntity entity : esEntities.getContent()) {
            contractNameMap.put(entity.getDataId(), entity.getData().getString(ContractEnum.NAME.getAttr()));
        }
        return contractNameMap;
    }

    @Override
    public Map<Long, String> getContractNoMap(List<Long> handoverIdSet, String corpid) throws XbbException {
        Map<Long, String> contractNoMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (handoverIdSet.isEmpty()) {
            return contractNoMap;
        }
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(), handoverIdSet));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_CONTRACT.getType()));
        XbbAggregatedPage<PaasFormDataEntity> esEntities = esHelper.findByPage(IndexTypeEnum.IDX_SAAS_CONTRACT, boolQueryBuilder, PaasFormDataEntity.class, Arrays.asList(FieldTypeEnum.DATAID.getAlias(), FieldTypeEnum.SERIALNO.getAlias()), 1, handoverIdSet.size(), null);
        for (PaasFormDataEntity entity : esEntities.getContent()) {
            contractNoMap.put(entity.getDataId(), entity.getSerialNo());
        }
        return contractNoMap;
    }

    @Override
    public void afterSaveForWorkflow(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        JSONObject newData = saasFormSaveDTO.getNewData();
        Long contractId = newPaasFormDataEntity.getId();
        String corpid = newPaasFormDataEntity.getCorpid();
        Boolean isNew = saasFormSaveDTO.getIsNew();
        Integer taskType = saasFormSaveDTO.getTaskType();

        // 团队保存
        teamDataHelp.saveUserTeam(saasFormSaveDTO.getSaasNeedRedundantAttrPoJo(), saasFormSaveDTO.getNewPaasFormDataEntity().getId(), saasFormSaveDTO.getNewPaasFormDataEntity(), saasFormSaveDTO.getIsImport(), true);

//        userTeamService.saveUserTeam(saasFormSaveDTO);
        // 以下业务应该是可以异步处理的业务
        Runnable runnable = () -> {
            // 合同关联产品保存
            try {
                JSONArray contractProduct = newData.getJSONArray(ContractEnum.PRODUCT.getAttr());
                if (contractProduct != null) {
                    if (saasFormSaveDTO.getSaasNeedRedundantAttrPoJo().getAttrCanSee(ContractEnum.PRODUCT.getAttr())) {
                        saveProduct(contractProduct, corpid, saasFormSaveDTO.getNewPaasFormDataEntity(), isNew,saasFormSaveDTO.getNewPaasFormDataEntity().getFormId(), saasFormSaveDTO.getDistributorMark(),taskType);
                    }
                }
            } catch (Exception e) {
                LOG.error("合同保存产品数据解析/合同保存产品关联产品", e);
            }

        };
        threadPoolBeanConfig.addUpdateDelThreadPool().execute(runnable);
    }

    @Override
    public void afterSaveForUpdateWorkflow(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        JSONObject newData = saasFormSaveDTO.getNewData();
        JSONObject oldData = saasFormSaveDTO.getOldData();
        Long contractId = newPaasFormDataEntity.getId();
        String corpid = newPaasFormDataEntity.getCorpid();
        Boolean isNew = saasFormSaveDTO.getIsNew();
        // 团队保存
        teamDataHelp.saveUserTeam(saasFormSaveDTO.getSaasNeedRedundantAttrPoJo(), saasFormSaveDTO.getNewPaasFormDataEntity().getId(), saasFormSaveDTO.getNewPaasFormDataEntity(), saasFormSaveDTO.getIsImport(), true);

        Integer taskType = saasFormSaveDTO.getTaskType();

        // userTeamService.saveUserTeam(saasFormSaveDTO);
        // if (Objects.nonNull(saasFormSaveDTO.getSaasNeedRedundantAttrPoJo()) &&
        //         CollectionUtils.isNotEmpty(saasFormSaveDTO.getSaasNeedRedundantAttrPoJo().getBeforeSaveMyTeamDTO().getApproveFailedList())) {
        //     List<Long> idInList = saasFormSaveDTO.getSaasNeedRedundantAttrPoJo().getBeforeSaveMyTeamDTO().getApproveFailedList();
        //     List<ContractUserEntity> list = new ArrayList<>();
        //     idInList.forEach(item -> {
        //         ContractUserEntity contractUserEntity = new ContractUserEntity();
        //         contractUserEntity.setId(item);
        //         contractUserEntity.setDataId(contractId);
        //         list.add(contractUserEntity);
        //     });
        //     contractUserModel.deleteBatchUser(list, corpid, DelEnum.DELETE.getDel());
        // }
        // 以下业务应该是可以异步处理的业务
        Runnable runnable = () -> {
            // 合同关联产品保存
            try {
                JSONArray contractProduct = newData.getJSONArray(ContractEnum.PRODUCT.getAttr());
                if (contractProduct != null) {
                    if (saasFormSaveDTO.getSaasNeedRedundantAttrPoJo().getAttrCanSee(ContractEnum.PRODUCT.getAttr())) {
                        saveProduct(contractProduct, corpid, newPaasFormDataEntity,isNew,saasFormSaveDTO.getNewPaasFormDataEntity().getFormId(), saasFormSaveDTO.getDistributorMark(),taskType);
                    }
                }
            } catch (Exception e) {
                LOG.error("合同保存产品数据解析/合同保存产品关联产品", e);
            }
        };
        threadPoolBeanConfig.addUpdateDelThreadPool().execute(runnable);
    }

    @Override
    public void updateContractDate(List<Map<String, Object>> updateContractEntity, String corpid) throws XbbException {
        //更新数据库信息
        contractModel.updateContractDate(updateContractEntity, corpid);
        //更新es信息
        List<UpdateDataEntity> list = new ArrayList<>();
        for (Map<String, Object> map : updateContractEntity) {
            UpdateDataEntity updateDataEntity = new UpdateDataEntity();
            updateDataEntity.setId((Long) map.get("id"));
            updateDataEntity.setCorpid(corpid);
            List<UpdateDataValueEntity> data = new ArrayList<>();
            for (String key : map.keySet()) {
                if (Objects.equals(key, "id")) {
                    continue;
                }
                UpdateDataValueEntity updateDataValueEntity = new UpdateDataValueEntity();
                updateDataValueEntity.setKey(key);
                updateDataValueEntity.setEsKey(key);
                updateDataValueEntity.setValue(map.get(key));
                data.add(updateDataValueEntity);
            }
            updateDataEntity.setData(data);
            list.add(updateDataEntity);
        }
        paasEsModel.updateBatchMuchField(list, IndexTypeEnum.IDX_SAAS_CONTRACT);
    }

    @Override
    public void formatWaitOutstockContractExportValue(List<PaasFormDataEntityExt> paasFormDataESList, FormDataListDTO formDataListDTO) {
        //封装待入库产品数据
        for (PaasFormDataEntityExt paasFormDataEntityExt : paasFormDataESList){
            JSONObject data = paasFormDataEntityExt.getData();
            JSONArray productJSONArray = new JSONArray();
            List<WaitOutstockProductPojo> waitOutstockProductPojoList = (List<WaitOutstockProductPojo>) data.getJSONObject("option_9").get("tableData");
            for (WaitOutstockProductPojo waitOutstockProductPojo : waitOutstockProductPojoList){
                JSONObject waitOutstockProduct = new JSONObject();
                waitOutstockProduct.put(WaitOutstockEnum.PRODUCT_NAME.getAttr(),waitOutstockProductPojo.getProductName());
                waitOutstockProduct.put(WaitOutstockEnum.PRODUCT_NO.getAttr(),waitOutstockProductPojo.getProductNo());
                waitOutstockProduct.put(WaitOutstockEnum.SPECIFICATION.getAttr(),waitOutstockProductPojo.getSpecification());
                waitOutstockProduct.put(WaitOutstockEnum.UNIT.getAttr(),waitOutstockProductPojo.getUnit());
                waitOutstockProduct.put(WaitOutstockEnum.STOCK.getAttr(),waitOutstockProductPojo.getStock());
                waitOutstockProduct.put(WaitOutstockEnum.WAIT_OUTSTOCK_NUM.getAttr(),waitOutstockProductPojo.getProductNum());
                waitOutstockProduct.put(WaitOutstockEnum.MEMO.getAttr(),waitOutstockProductPojo.getMemo());
                productJSONArray.add(waitOutstockProduct);
            }
            data.put(ContractEnum.PRODUCT.getAttr(),productJSONArray);
            String name = data.getJSONArray(WaitOutstockEnum.LINK_CUSTOMER.getAttr()).getJSONObject(0).getString("name");
            data.put(WaitOutstockEnum.LINK_CUSTOMER.getAttr(),name);
            String addTime = DateTimeUtil.getStringEpochSecond(data.getLong(ContractEnum.ADD_TIME.getAttr()), DateTimeUtil.SDF);
            String updateTime = DateTimeUtil.getStringEpochSecond(data.getLong(ContractEnum.UPDATE_TIME.getAttr()), DateTimeUtil.SDF);
            String signTime = DateTimeUtil.getStringEpochSecond(data.getLong(WaitOutstockEnum.SIGN_TIME.getAttr()), DateTimeUtil.SDFDate);
            data.put(ContractEnum.ADD_TIME.getAttr(),addTime);
            data.put(ContractEnum.UPDATE_TIME.getAttr(),updateTime);
            data.put(ContractEnum.SIGN_TIME.getAttr(),signTime);
            data.remove("ption_9");
        }
    }

    /**
     * 处理待出库移动端详情的数据权限
     */
    @Override
    public List<FieldAttrEntity> processContractAndproductExplain(List<PaasFormDataEntityExt> productPaasFormDatas,Map<Long, PaasFormDataEntity> contractMap, List<ContractProductEntity> contractProductEntityList, UserVO loginUser, String corpid, Long formId, String userId, StringBuffer coUserListStr, StringBuffer mainUserListStr) throws XbbException {
        PaasFormExplainEntity contractExplainEntity = paasFormExplainModel.getByFormId(formId, corpid);
        List<FieldAttrEntity> contractExplainList = JSONArray.parseArray(contractExplainEntity.getExplains(), FieldAttrEntity.class);
        //分别获取合同和产品字段的解释
        List<FieldAttrEntity> explainList = new ArrayList<>();
        contractExplainList.forEach(fieldAttrEntity ->{
            String attr = fieldAttrEntity.getAttr();
            if (Objects.equals(attr, ContractEnum.NAME.getAttr())){
                explainList.add(fieldAttrEntity);
            }else if (Objects.equals(attr, ContractEnum.CONTRACT_NO.getAttr())){
                explainList.add(fieldAttrEntity);
            }else if (Objects.equals(attr, ContractEnum.LINK_CUSTOMER.getAttr())){
                explainList.add(fieldAttrEntity);
            }else if (Objects.equals(attr, ContractEnum.SIGN_TIME.getAttr())){
                explainList.add(fieldAttrEntity);
            }else if (Objects.equals(attr, ContractEnum.STATUS.getAttr())){
                explainList.add(fieldAttrEntity);
            }else if (Objects.equals(attr, ContractEnum.ADD_TIME.getAttr())){
                explainList.add(fieldAttrEntity);
            }else if (Objects.equals(attr, ContractEnum.UPDATE_TIME.getAttr())){
                explainList.add(fieldAttrEntity);
            } else if (Objects.equals(attr, ContractEnum.CREATOR_ID.getAttr())) {
                explainList.add(fieldAttrEntity);
            } else if (Objects.equals(attr, ContractEnum.OWNER_ID.getAttr())) {
                explainList.add(fieldAttrEntity);
            } else if (Objects.equals(attr, ContractEnum.MINOR_USER.getAttr())) {
                explainList.add(fieldAttrEntity);
            } else if (Objects.equals(attr, ContractEnum.PRODUCT.getAttr())){
                List<FieldAttrEntity> items = (List<FieldAttrEntity>) fieldAttrEntity.getSubForm().getItems();
                items.forEach(fieldAttrEnt -> {
                    if(Objects.equals(fieldAttrEnt.getAttr(),ContractProductEnum.UNIT.getAttr())){
                        fieldAttrEnt.setAttr(WaitOutstockEnum.UNIT.getAttr());
                        fieldAttrEnt.setAttrName(WaitOutstockEnum.UNIT.getAttrName());
                        explainList.add(fieldAttrEnt);
                    } else if(Objects.equals(fieldAttrEnt.getAttr(),ContractProductEnum.MEMO.getAttr())){
                        fieldAttrEnt.setAttr(WaitOutstockEnum.MEMO.getAttr());
                        fieldAttrEnt.setAttrName(WaitOutstockEnum.MEMO.getAttrName());
                        explainList.add(fieldAttrEnt);
                    } else if(Objects.equals(fieldAttrEnt.getAttr(),ContractProductEnum.STOCK.getAttr())){
                        fieldAttrEnt.setAttr(WaitOutstockEnum.STOCK.getAttr());
                        fieldAttrEnt.setAttrName(WaitOutstockEnum.STOCK.getAttrName());
                        explainList.add(fieldAttrEnt);
                    }
                });
            }
        });
        //开始处理权限
        Map<String, FieldAttrEntity> expalinMap = ExplainUtil.getExplainMapByList(explainList);
        UserAndDepartmentGetDTO userAndDepartmentGetDTO = new UserAndDepartmentGetDTO();
        userAndDepartmentGetDTO.setLoginUser(loginUser);
        userAndDepartmentGetDTO.setUserId(userId);
        userAndDepartmentGetDTO.setCorpid(corpid);
        UserAndDepartmentGetVO userAndDepartmentGetVO = userHelp.getUserDepartmentMap(userAndDepartmentGetDTO);
        Map<String, UserEntity> userMap = userAndDepartmentGetVO.getUserMap();
        Map<String, DepartmentEntity> departmentMap = userAndDepartmentGetVO.getDepartmentMap();
        ParseSingleRowDataDTO parseSingleRowDataDTO = new ParseSingleRowDataDTO();
        parseSingleRowDataDTO.setExplainMap(expalinMap);
        parseSingleRowDataDTO.setUserMap(userMap);
        parseSingleRowDataDTO.setDepartmentMap(departmentMap);
        parseSingleRowDataDTO.setLoginUser(loginUser);
        parseSingleRowDataDTO.setUserId(userId);
        parseSingleRowDataDTO.setCorpid(corpid);
        //循环处理
        Set<Long> set = contractMap.keySet();
        for (Long contractId : set) {
            //协同团队
            Map<Long, List<String>> mainUserMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, List<String>> coUserMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            userTeamService.getUserIdMap(Collections.singletonList(contractId),corpid,XbbRefTypeEnum.CONTRACT.getCode(),false,mainUserMap,coUserMap);
            List<UserEntity> coUserList = new ArrayList<>();
            List<UserEntity> mainUserList = new ArrayList<>();
            if(coUserMap.get(contractId)!=null){
                Map<String, Object> paramMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                paramMap.put(ParameterConstant.LEAVE_MARKS,1);
                paramMap.put("corpid",corpid);
                paramMap.put("userIdIn",coUserMap.get(contractId));
                coUserList = userModel.findEntitys(paramMap);
            }
            for (int i = 0; i < coUserList.size(); i++) {
                UserEntity temp = coUserList.get(i);
                if (i < (coUserList.size() - 1)) {
                    coUserListStr.append(temp.getName() + "、");
                } else {
                    coUserListStr.append(temp.getName());
                }
            }
            if (mainUserMap.get(contractId) != null) {
                Map<String, Object> paramMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                paramMap.put(ParameterConstant.LEAVE_MARKS,1);
                paramMap.put("corpid",corpid);
                paramMap.put("userIdIn",mainUserMap.get(contractId));
                mainUserList = userModel.findEntitys(paramMap);
            }
            for (int i = 0; i < mainUserList.size(); i++) {
                UserEntity temp = mainUserList.get(i);
                if (i < (mainUserList.size() - 1)) {
                    mainUserListStr.append(temp.getName() + "、");
                } else {
                    mainUserListStr.append(temp.getName());
                }
            }
            PaasFormDataEntity contractDadaEntity = contractMap.get(contractId);
            Map<String, Boolean> canSeeMap = saasListHelp.parseCanseeParam(explainList, contractDadaEntity.getCreatorId(), Collections.singletonList(contractDadaEntity.getOwnerId()), new ArrayList<>(), parseSingleRowDataDTO);
            JSONObject data = contractDadaEntity.getData();
            List<ContractProductEntity>  contractProductEntities=contractProductEntityList.stream().filter(p ->Objects.equals(p.getContractId(),contractId)).collect(Collectors.toList());
            for (Map.Entry<String, Boolean> stringBooleanEntry : canSeeMap.entrySet()) {
                String key = stringBooleanEntry.getKey();
                Boolean value = stringBooleanEntry.getValue();
                if (!value ){
                    if (Objects.equals(key, ContractEnum.CONTRACT_NO.getAttr())){
                        contractDadaEntity.setSerialNo("");
                    }else if(Objects.equals(key, ContractEnum.NAME.getAttr())){
                        data.put(ContractEnum.NAME.getAttr(), "");
                    }else if(Objects.equals(key, ContractEnum.LINK_CUSTOMER.getAttr())){
                        data.put(ContractEnum.LINK_CUSTOMER.getAttr(), "");
                        data.put(ContractEnum.LINK_CUSTOMER_HIDE.getAttr(), "");
                    }else if(Objects.equals(key, WaitOutstockEnum.SIGN_TIME.getAttr())){
                        data.put(WaitOutstockEnum.SIGN_TIME.getAttr(), "");
                    }else if(Objects.equals(key, WaitOutstockEnum.STATUS.getAttr())){
                        data.put(WaitOutstockEnum.STATUS.getAttr(), "");
                    }else if(Objects.equals(key, WaitOutstockEnum.ADD_TIME.getAttr())){
                        contractDadaEntity.setAddTime(null);
                    }else if(Objects.equals(key, WaitOutstockEnum.UPDATE_TIME.getAttr())){
                        contractDadaEntity.setUpdateTime(null);
                    }else if(Objects.equals(key, WaitOutstockEnum.UNIT.getAttr())){
                        contractProductEntities.forEach(contractProductEntity ->{
                            contractProductEntity.setProductUnit(null);
                        });
                    }else if(Objects.equals(key, WaitOutstockEnum.STOCK.getAttr())){
                        if(Objects.nonNull(productPaasFormDatas)){
                            productPaasFormDatas.forEach(paasFormDataEntityExt ->{
                                paasFormDataEntityExt.getData().put(ProductEnum.STOCK.getAttr(),"");
                            });
                        }
                    } else if(Objects.equals(key, WaitOutstockEnum.CREATOR_ID.getAttr())){
                        contractDadaEntity.setCreatorId(null);
                    } else if (Objects.equals(key, WaitOutstockEnum.OWNER_ID.getAttr())) {
                        contractDadaEntity.setOwnerId(null);
                        mainUserListStr.setLength(0);
                    } else if (Objects.equals(key, WaitOutstockEnum.MINOR_USER.getAttr())) {
                        coUserListStr.setLength(0);
                    }else if(Objects.equals(key, WaitOutstockEnum.MEMO.getAttr())){
                        contractProductEntities.forEach(contractProductEntity ->{
                            contractProductEntity.setMemo("");
                        });
                    }
                }
            }
        }
        return explainList;
    }

    @Override
    public void dataConsistencyUpdateOpportunity(String corpid, Long dataId, String serialNo, String attr) {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0 ))
                    .filter(termQuery("data." + ContractEnum.LINK_OPPORTUNITY.getAttr() + ".keyword", dataId));
            List<String> fieldList = new ArrayList<>();
            fieldList.add(FieldTypeEnum.DATAID.getAlias());
            List<PaasFormDataEntityExt> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CONTRACT, filter, PaasFormDataEntityExt.class, fieldList);
            Set<Long> ids = new HashSet<>();
            for (PaasFormDataEntityExt esEntity : esEntities) {
                ids.add(esEntity.getDataId());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("idIn", ids);
            param.put("del", 0);
            List<ContractEntityExt> entitys = contractModel.findEntitys(param);

            if (CollectionsUtil.isNotEmpty(entitys)) {
                List<ContractUpdateDTO> contractUpdateDTOList = new ArrayList<>();
                for (ContractEntityExt entityExt : entitys) {
                    ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
                    contractUpdateDTO.setId(entityExt.getId());
                    JSONObject data = new JSONObject();
                    data.put(ContractEnum.LINK_OPPORTUNITY_HIDE.getAttr(), serialNo);
                    contractUpdateDTO.setData(data);
                    contractUpdateDTOList.add(contractUpdateDTO);
                }
                ContractUpdateBatchDTO contractUpdateBatchDTO = new ContractUpdateBatchDTO();
                contractUpdateBatchDTO.setCorpid(corpid);
                contractUpdateBatchDTO.setContractUpdateList(contractUpdateDTOList);
                updateBatch(contractUpdateBatchDTO);
            }

        } catch (Exception e) {
            LOG.error("ContractServiceImpl.dataConsistencyUpdateOpportunity 出错， corpid=" + corpid + " linkId=" + dataId + " linkName=" + ContractEnum.LINK_OPPORTUNITY_HIDE.getAttrName() + " linkAttr=" + ContractEnum.LINK_OPPORTUNITY_HIDE.getAttr(), e);
        }
    }
}
